Update fuchsia tty to work with latest

Change-Id: I930c7ec67ad87d7dee13f30fefdd673d2216aadf
diff --git a/src/tty/fuchsia.rs b/src/tty/fuchsia.rs
index a63723b..febb577 100644
--- a/src/tty/fuchsia.rs
+++ b/src/tty/fuchsia.rs
@@ -3,17 +3,22 @@
 use std::mem;
 use std::sync::atomic;
 use libc;
-use char_iter;
+use unicode_segmentation::UnicodeSegmentation;
+use utf8parse::{Parser, Receiver};
 
-use config::Config;
-use consts::{self, KeyPress};
+use config::{ColorMode, Config, OutputStreamType};
+use highlight::Highlighter;
+use keys::{self, KeyPress};
+use line_buffer::LineBuffer;
 use error;
 use Result;
-use super::{RawMode, RawReader, Term};
+use super::{truncate, width, Position, RawMode, RawReader, Renderer, Term};
 use fuchsia_device::pty;
+use StdStream;
 
-const STDIN_FILENO: usize = 0;
-const STDOUT_FILENO: usize = 1;
+const STDIN_FILENO: libc::c_int = libc::STDIN_FILENO;
+const STDOUT_FILENO: libc::c_int = libc::STDOUT_FILENO;
+const STDERR_FILENO: libc::c_int = libc::STDERR_FILENO;
 
 fn get_win_size() -> (usize, usize) {
     match pty::get_window_size() {
@@ -60,185 +65,522 @@
     }
 }
 
-pub type Terminal = Console;
+pub type Terminal = FuchsiaTerminal;
 
 #[derive(Clone, Debug)]
-pub struct Console {
+pub struct FuchsiaTerminal {
+    unsupported: bool,
     stdin_isatty: bool,
+    stdstream_isatty: bool,
+    pub(crate) color_mode: ColorMode,
+    stream_type: OutputStreamType,
+}
+
+struct Utf8 {
+    c: Option<char>,
+    valid: bool,
 }
 
 pub struct FuchsiaRawReader {
-    chars: char_iter::Chars<StdinRaw>,
+    stdin: StdinRaw,
+    timeout_ms: i32,
+    buf: [u8; 1],
+    parser: Parser,
+    receiver: Utf8,
 }
 
 impl FuchsiaRawReader {
-    pub fn new() -> Result<FuchsiaRawReader> {
-        let stdin = StdinRaw {};
+    pub fn new(config: &Config) -> Result<FuchsiaRawReader> {
         Ok(FuchsiaRawReader {
-            chars: char_iter::chars(stdin),
+            stdin: StdinRaw {},
+            timeout_ms: config.keyseq_timeout(),
+            buf: [0; 1],
+            parser: Parser::new(),
+            receiver: Utf8 {
+                c: None,
+                valid: true,
+            }
         })
     }
+
+    /// Handle ESC <seq1> sequences
     fn escape_sequence(&mut self) -> Result<KeyPress> {
-        // Read the next two bytes representing the escape sequence.
+        // Read the next byte 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 == '~' {
-                    Ok(match seq2 {
-                        '1' | '7' => KeyPress::Home, // '1': xterm
-                        '3' => KeyPress::Delete,
-                        '4' | '8' => KeyPress::End, // '4': xterm
-                        '5' => KeyPress::PageUp,
-                        '6' => KeyPress::PageDown,
-                        _ => {
-                            debug!(target: "rustyline", "unsupported esc sequence: ESC{:?}{:?}{:?}", seq1, seq2, seq3);
-                            KeyPress::UnknownEscSeq
-                        }
-                    })
-                } else {
-                    debug!(target: "rustyline", "unsupported esc sequence: ESC{:?}{:?}{:?}", seq1, seq2, seq3);
+            // ESC [ sequences. (CSI)
+            self.escape_csi()
+        } else if seq1 == 'O' {
+            // xterm
+            // ESC O sequences. (SS3)
+            self.escape_o()
+        } else if seq1 == '\x1b' {
+            // ESC ESC
+            Ok(KeyPress::Esc)
+        } else {
+            // TODO ESC-R (r): Undo all changes made to this line.
+            Ok(KeyPress::Meta(seq1))
+        }
+    }
+
+    /// Handle ESC [ <seq2> escape sequences
+    fn escape_csi(&mut self) -> Result<KeyPress> {
+        let seq2 = try!(self.next_char());
+        if seq2.is_digit(10) {
+            match seq2 {
+                '0' | '9' => {
+                    debug!(target: "rustyline", "unsupported esc sequence: ESC [ {:?}", seq2);
                     Ok(KeyPress::UnknownEscSeq)
                 }
-            } else {
-                Ok(match seq2 {
-                    'A' => KeyPress::Up, // ANSI
-                    'B' => KeyPress::Down,
-                    'C' => KeyPress::Right,
-                    'D' => KeyPress::Left,
-                    'F' => KeyPress::End,
-                    'H' => KeyPress::Home,
-                    _ => {
-                        debug!(target: "rustyline", "unsupported esc sequence: ESC{:?}{:?}", seq1, seq2);
-                        KeyPress::UnknownEscSeq
-                    }
-                })
-            }
-        } else if seq1 == 'O' {
-            // ESC O sequences.
-            let seq2 = try!(self.next_char());
-            Ok(match seq2 {
-                'A' => KeyPress::Up,
-                'B' => KeyPress::Down,
-                'C' => KeyPress::Right,
-                'D' => KeyPress::Left,
-                'F' => KeyPress::End,
-                'H' => KeyPress::Home,
                 _ => {
-                    debug!(target: "rustyline", "unsupported esc sequence: ESC{:?}{:?}", seq1, seq2);
+                    // Extended escape, read additional byte.
+                    self.extended_escape(seq2)
+                }
+            }
+        } else if seq2 == '[' {
+            let seq3 = try!(self.next_char());
+            // Linux console
+            Ok(match seq3 {
+                'A' => KeyPress::F(1),
+                'B' => KeyPress::F(2),
+                'C' => KeyPress::F(3),
+                'D' => KeyPress::F(4),
+                'E' => KeyPress::F(5),
+                _ => {
+                    debug!(target: "rustyline", "unsupported esc sequence: ESC [ [ {:?}", seq3);
                     KeyPress::UnknownEscSeq
                 }
             })
         } else {
-            // TODO ESC-R (r): Undo all changes made to this line.
-            Ok(match seq1 {
-                '\x08' => KeyPress::Meta('\x08'), // Backspace
-                '-' => KeyPress::Meta('-'),
-                '0'...'9' => KeyPress::Meta(seq1),
-                '<' => KeyPress::Meta('<'),
-                '>' => KeyPress::Meta('>'),
-                'b' | 'B' => KeyPress::Meta('B'),
-                'c' | 'C' => KeyPress::Meta('C'),
-                'd' | 'D' => KeyPress::Meta('D'),
-                'f' | 'F' => KeyPress::Meta('F'),
-                'l' | 'L' => KeyPress::Meta('L'),
-                'n' | 'N' => KeyPress::Meta('N'),
-                'p' | 'P' => KeyPress::Meta('P'),
-                't' | 'T' => KeyPress::Meta('T'),
-                'u' | 'U' => KeyPress::Meta('U'),
-                'y' | 'Y' => KeyPress::Meta('Y'),
-                '\x7f' => KeyPress::Meta('\x7f'), // Delete
+            // ANSI
+            Ok(match seq2 {
+                'A' => KeyPress::Up,    // kcuu1
+                'B' => KeyPress::Down,  // kcud1
+                'C' => KeyPress::Right, // kcuf1
+                'D' => KeyPress::Left,  // kcub1
+                'F' => KeyPress::End,
+                'H' => KeyPress::Home, // khome
+                'Z' => KeyPress::BackTab,
                 _ => {
-                    debug!(target: "rustyline", "unsupported esc sequence: M-{:?}", seq1);
+                    debug!(target: "rustyline", "unsupported esc sequence: ESC [ {:?}", seq2);
                     KeyPress::UnknownEscSeq
                 }
             })
         }
     }
+
+    /// Handle ESC [ <seq2:digit> escape sequences
+    fn extended_escape(&mut self, seq2: char) -> Result<KeyPress> {
+        let seq3 = try!(self.next_char());
+        if seq3 == '~' {
+            Ok(match seq2 {
+                '1' | '7' => KeyPress::Home, // tmux, xrvt
+                '2' => KeyPress::Insert,
+                '3' => KeyPress::Delete,    // kdch1
+                '4' | '8' => KeyPress::End, // tmux, xrvt
+                '5' => KeyPress::PageUp,    // kpp
+                '6' => KeyPress::PageDown,  // knp
+                _ => {
+                    debug!(target: "rustyline",
+                           "unsupported esc sequence: ESC [ {} ~", seq2);
+                    KeyPress::UnknownEscSeq
+                }
+            })
+        } else if seq3.is_digit(10) {
+            let seq4 = try!(self.next_char());
+            if seq4 == '~' {
+                Ok(match (seq2, seq3) {
+                    ('1', '1') => KeyPress::F(1),  // rxvt-unicode
+                    ('1', '2') => KeyPress::F(2),  // rxvt-unicode
+                    ('1', '3') => KeyPress::F(3),  // rxvt-unicode
+                    ('1', '4') => KeyPress::F(4),  // rxvt-unicode
+                    ('1', '5') => KeyPress::F(5),  // kf5
+                    ('1', '7') => KeyPress::F(6),  // kf6
+                    ('1', '8') => KeyPress::F(7),  // kf7
+                    ('1', '9') => KeyPress::F(8),  // kf8
+                    ('2', '0') => KeyPress::F(9),  // kf9
+                    ('2', '1') => KeyPress::F(10), // kf10
+                    ('2', '3') => KeyPress::F(11), // kf11
+                    ('2', '4') => KeyPress::F(12), // kf12
+                    _ => {
+                        debug!(target: "rustyline",
+                               "unsupported esc sequence: ESC [ {}{} ~", seq2, seq3);
+                        KeyPress::UnknownEscSeq
+                    }
+                })
+            } else if seq4 == ';' {
+                let seq5 = try!(self.next_char());
+                if seq5.is_digit(10) {
+                    let seq6 = try!(self.next_char()); // '~' expected
+                    debug!(target: "rustyline",
+                           "unsupported esc sequence: ESC [ {}{} ; {} {}", seq2, seq3, seq5, seq6);
+                } else {
+                    debug!(target: "rustyline",
+                           "unsupported esc sequence: ESC [ {}{} ; {:?}", seq2, seq3, seq5);
+                }
+                Ok(KeyPress::UnknownEscSeq)
+            } else {
+                debug!(target: "rustyline",
+                       "unsupported esc sequence: ESC [ {}{} {:?}", seq2, seq3, seq4);
+                Ok(KeyPress::UnknownEscSeq)
+            }
+        } else if seq3 == ';' {
+            let seq4 = try!(self.next_char());
+            if seq4.is_digit(10) {
+                let seq5 = try!(self.next_char());
+                if seq2 == '1' {
+                    Ok(match (seq4, seq5) {
+                        ('5', 'A') => KeyPress::ControlUp,
+                        ('5', 'B') => KeyPress::ControlDown,
+                        ('5', 'C') => KeyPress::ControlRight,
+                        ('5', 'D') => KeyPress::ControlLeft,
+                        ('2', 'A') => KeyPress::ShiftUp,
+                        ('2', 'B') => KeyPress::ShiftDown,
+                        ('2', 'C') => KeyPress::ShiftRight,
+                        ('2', 'D') => KeyPress::ShiftLeft,
+                        _ => {
+                            debug!(target: "rustyline",
+                                   "unsupported esc sequence: ESC [ 1 ; {} {:?}", seq4, seq5);
+                            KeyPress::UnknownEscSeq
+                        }
+                    })
+                } else {
+                    debug!(target: "rustyline",
+                           "unsupported esc sequence: ESC [ {} ; {} {:?}", seq2, seq4, seq5);
+                    Ok(KeyPress::UnknownEscSeq)
+                }
+            } else {
+                debug!(target: "rustyline",
+                       "unsupported esc sequence: ESC [ {} ; {:?}", seq2, seq4);
+                Ok(KeyPress::UnknownEscSeq)
+            }
+        } else {
+            Ok(match (seq2, seq3) {
+                ('5', 'A') => KeyPress::ControlUp,
+                ('5', 'B') => KeyPress::ControlDown,
+                ('5', 'C') => KeyPress::ControlRight,
+                ('5', 'D') => KeyPress::ControlLeft,
+                _ => {
+                    debug!(target: "rustyline",
+                           "unsupported esc sequence: ESC [ {} {:?}", seq2, seq3);
+                    KeyPress::UnknownEscSeq
+                }
+            })
+        }
+    }
+
+    /// Handle ESC O <seq2> escape sequences
+    fn escape_o(&mut self) -> Result<KeyPress> {
+        let seq2 = try!(self.next_char());
+        Ok(match seq2 {
+            'A' => KeyPress::Up,    // kcuu1
+            'B' => KeyPress::Down,  // kcud1
+            'C' => KeyPress::Right, // kcuf1
+            'D' => KeyPress::Left,  // kcub1
+            'F' => KeyPress::End,   // kend
+            'H' => KeyPress::Home,  // khome
+            'P' => KeyPress::F(1),  // kf1
+            'Q' => KeyPress::F(2),  // kf2
+            'R' => KeyPress::F(3),  // kf3
+            'S' => KeyPress::F(4),  // kf4
+            'a' => KeyPress::ControlUp,
+            'b' => KeyPress::ControlDown,
+            'c' => KeyPress::ControlRight, // rxvt
+            'd' => KeyPress::ControlLeft,  // rxvt
+            _ => {
+                debug!(target: "rustyline", "unsupported esc sequence: ESC O {:?}", seq2);
+                KeyPress::UnknownEscSeq
+            }
+        })
+    }
 }
 
-// TODO properly set raw mode, process escape keys
+pub struct FuchsiaRenderer {
+    out: StdStream,
+    cols: usize,
+    buffer: String,
+}
+
+impl FuchsiaRenderer {
+    pub fn new(stream_type: OutputStreamType) -> FuchsiaRenderer {
+        let out = StdStream::from_stream_type(stream_type);
+        let (cols, _) = get_win_size();
+        FuchsiaRenderer {
+            out,
+            cols,
+            buffer: String::with_capacity(1024),
+        }
+    }
+}
+
+impl Renderer for FuchsiaRenderer {
+    fn move_cursor(&mut self, old: Position, new: Position) -> Result<()> {
+        use std::fmt::Write;
+        let mut ab = String::new();
+        if new.row > old.row {
+            // move down
+            let row_shift = new.row - old.row;
+            if row_shift == 1 {
+                ab.push_str("\x1b[B");
+            } else {
+                write!(ab, "\x1b[{}B", row_shift).unwrap();
+            }
+        } else if new.row < old.row {
+            // move up
+            let row_shift = old.row - new.row;
+            if row_shift == 1 {
+                ab.push_str("\x1b[A");
+            } else {
+                write!(ab, "\x1b[{}A", row_shift).unwrap();
+            }
+        }
+        if new.col > old.col {
+            // move right
+            let col_shift = new.col - old.col;
+            if col_shift == 1 {
+                ab.push_str("\x1b[C");
+            } else {
+                write!(ab, "\x1b[{}C", col_shift).unwrap();
+            }
+        } else if new.col < old.col {
+            // move left
+            let col_shift = old.col - new.col;
+            if col_shift == 1 {
+                ab.push_str("\x1b[D");
+            } else {
+                write!(ab, "\x1b[{}D", col_shift).unwrap();
+            }
+        }
+        self.write_and_flush(ab.as_bytes())
+    }
+
+    /// Display `prompt`, line and cursor in terminal output
+    fn refresh_line(
+        &mut self,
+        prompt: &str,
+        prompt_size: Position,
+        line: &LineBuffer,
+        hint: Option<String>,
+        current_row: usize,
+        old_rows: usize,
+        highlighter: Option<&Highlighter>,
+    ) -> Result<(Position, Position)> {
+        use std::fmt::Write;
+        self.buffer.clear();
+
+        // calculate the position of the end of the input line
+        let end_pos = self.calculate_position(line, prompt_size);
+        // calculate the desired position of the cursor
+        let cursor = self.calculate_position(&line[..line.pos()], prompt_size);
+
+        // self.old_rows < self.cursor.row if the prompt spans multiple lines and if
+        // this is the default State.
+        let cursor_row_movement = old_rows.checked_sub(current_row).unwrap_or(0);
+        // move the cursor down as required
+        if cursor_row_movement > 0 {
+            write!(self.buffer, "\x1b[{}B", cursor_row_movement).unwrap();
+        }
+        // clear old rows
+        for _ in 0..old_rows {
+            self.buffer.push_str("\r\x1b[0K\x1b[A");
+        }
+        // clear the line
+        self.buffer.push_str("\r\x1b[0K");
+
+        if let Some(highlighter) = highlighter {
+            // display the prompt
+            self.buffer.push_str(&highlighter.highlight_prompt(prompt));
+            // display the input line
+            self.buffer
+                .push_str(&highlighter.highlight(line, line.pos()));
+        } else {
+            // display the prompt
+            self.buffer.push_str(prompt);
+            // display the input line
+            self.buffer.push_str(line);
+        }
+        // display hint
+        if let Some(hint) = hint {
+            let truncate = truncate(&hint, end_pos.col, self.cols);
+            if let Some(highlighter) = highlighter {
+                self.buffer.push_str(&highlighter.highlight_hint(truncate));
+            } else {
+                self.buffer.push_str(truncate);
+            }
+        }
+        // we have to generate our own newline on line wrap
+        if end_pos.col == 0 && end_pos.row > 0 {
+            self.buffer.push_str("\n");
+        }
+        // position the cursor
+        let cursor_row_movement = end_pos.row - cursor.row;
+        // move the cursor up as required
+        if cursor_row_movement > 0 {
+            write!(self.buffer, "\x1b[{}A", cursor_row_movement).unwrap();
+        }
+        // position the cursor within the line
+        if cursor.col > 0 {
+            write!(self.buffer, "\r\x1b[{}C", cursor.col).unwrap();
+        } else {
+            self.buffer.push('\r');
+        }
+
+        try!(self.out.write_all(self.buffer.as_bytes()));
+        try!(self.out.flush());
+        Ok((cursor, end_pos))
+    }
+
+    /// Calculate the number of columns and rows used to display `s` on a
+    /// `cols` width terminal starting at `orig`.
+    fn calculate_position(&self, s: &str, orig: Position) -> Position {
+        let mut pos = orig;
+        let mut esc_seq = 0;
+        for c in s.graphemes(true) {
+            if c == "\n" {
+                pos.row += 1;
+                pos.col = 0;
+                continue;
+            }
+            let cw = width(c, &mut esc_seq);
+            pos.col += cw;
+            if pos.col > self.cols {
+                pos.row += 1;
+                pos.col = cw;
+            }
+        }
+        if pos.col == self.cols {
+            pos.col = 0;
+            pos.row += 1;
+        }
+        pos
+    }
+
+    fn write_and_flush(&mut self, buf: &[u8]) -> Result<()> {
+        try!(self.out.write_all(buf));
+        try!(self.out.flush());
+        Ok(())
+    }
+
+    /// Clear the screen. Used to handle ctrl+l
+    fn clear_screen(&mut self) -> Result<()> {
+        self.write_and_flush(b"\x1b[H\x1b[2J")
+    }
+
+    /// Check if a SIGWINCH signal has been received
+    fn sigwinch(&self) -> bool {
+        false
+    }
+
+    /// Update the number of columns/rows in the current terminal.
+    fn update_size(&mut self) {
+        let (cols, _) = get_win_size();
+        self.cols = cols;
+    }
+
+    /// Get the number of columns in the current terminal.
+    fn get_columns(&self) -> usize {
+        let (cols, _) = get_win_size();
+        cols
+    }
+
+    /// Get the number of rows in the current terminal.
+    fn get_rows(&self) -> usize {
+        let (_, rows) = get_win_size();
+        rows
+    }
+}
+
+
+// TODO properly set raw mode, handle escape key timeouts
 
 impl RawReader for FuchsiaRawReader {
-    fn next_key(&mut self) -> Result<KeyPress> {
+    fn next_key(&mut self, single_esc_abort: bool) -> Result<KeyPress> {
         let c = try!(self.next_char());
 
-        let mut key = consts::char_to_key_press(c);
+        let mut key = keys::char_to_key_press(c);
         if key == KeyPress::Esc {
-            // TODO
-            debug!(target: "rustyline", "ESC + {:?} currently unsupported", key);
+            return self.escape_sequence();
         }
 
         Ok(key)
     }
 
     fn next_char(&mut self) -> Result<char> {
-        match self.chars.next() {
-            Some(ch) => Ok(ch?),
-            None => Err(error::ReadlineError::Eof),
+        loop {
+            let n = try!(self.stdin.read(&mut self.buf));
+            if n == 0 {
+                return Err(error::ReadlineError::Eof);
+            }
+            let b = self.buf[0];
+            self.parser.advance(&mut self.receiver, b);
+            if !self.receiver.valid {
+                return Err(error::ReadlineError::Utf8Error);
+            } else if self.receiver.c.is_some() {
+                return Ok(self.receiver.c.take().unwrap());
+            }
         }
     }
 }
 
-impl Term for Console {
-    type Reader = FuchsiaRawReader;
-    type Writer = Stdout;
-    type Mode = Mode;
-
-    fn new() -> Console {
-        let stdin_isatty = true;
-        Console {
-            stdin_isatty: stdin_isatty,
-        }
+impl Receiver for Utf8 {
+    /// Called whenever a codepoint is parsed successfully
+    fn codepoint(&mut self, c: char) {
+        self.c = Some(c);
+        self.valid = true;
     }
 
-    /// Checking for an unsupported TERM in fuchsia is a no-op
+    /// Called when an invalid_sequence is detected
+    fn invalid_sequence(&mut self) {
+        self.c = None;
+        self.valid = false;
+    }
+}
+
+fn is_a_tty(fd: libc::c_int) -> bool {
+    unsafe { libc::isatty(fd) != 0 }
+}
+
+impl Term for FuchsiaTerminal {
+    type Reader = FuchsiaRawReader;
+    type Writer = FuchsiaRenderer;
+    type Mode = Mode;
+
+    fn new(color_mode: ColorMode, stream_type: OutputStreamType) -> FuchsiaTerminal {
+        FuchsiaTerminal {
+            unsupported: false,
+            stdin_isatty: is_a_tty(STDIN_FILENO as i32),
+            stdstream_isatty: is_a_tty(if stream_type == OutputStreamType::Stdout {
+                STDOUT_FILENO as i32
+            } else {
+                STDERR_FILENO as i32
+            }),
+            color_mode,
+            stream_type,
+        }
+    }
+    /// Check if current terminal can provide a rich line-editing user
+    /// interface.
     fn is_unsupported(&self) -> bool {
         false
     }
-
+    /// check if stdin is connected to a terminal.
     fn is_stdin_tty(&self) -> bool {
         self.stdin_isatty
     }
-
-    /// Try to get the number of columns in the current terminal,
-    /// or assume 80 if it fails.
-    fn get_columns(&self) -> usize {
-        let (cols, _) = get_win_size();
-        cols
+    /// Check if output supports colors.
+    fn colors_enabled(&self) -> bool {
+        true
     }
-
-    /// Try to get the number of rows in the current terminal,
-    /// or assume 24 if it fails.
-    fn get_rows(&self) -> usize {
-        let (_, rows) = get_win_size();
-        rows
-    }
-
-    /// Enable RAW mode for the terminal. No termios support, so this fakes it
-    fn enable_raw_mode(&self) -> Result<Mode> {
+    /// Enable RAW mode for the terminal.
+    fn enable_raw_mode(&mut self) -> Result<Self::Mode> {
         Ok(Mode {})
     }
-
-    fn create_reader(&self, _: &Config) -> Result<FuchsiaRawReader> {
-        FuchsiaRawReader::new()
+    /// Create a RAW reader
+    fn create_reader(&self, config: &Config) -> Result<Self::Reader> {
+        FuchsiaRawReader::new(config)
     }
-
-    fn create_writer(&self) -> Stdout {
-        io::stdout()
-    }
-
-    fn sigwinch(&self) -> bool {
-        false
-    }
-
-    /// Clear the screen. Used to handle ctrl+l
-    fn clear_screen(&mut self, w: &mut Write) -> Result<()> {
-        try!(w.write_all(b"\x1b[H\x1b[2J"));
-        try!(w.flush());
-        Ok(())
+    /// Create a writer
+    fn create_writer(&self) -> Self::Writer {
+        FuchsiaRenderer::new(self.stream_type)
     }
 }