diff options
author | Joe Wilm <joe@jwilm.com> | 2016-05-30 20:44:37 -0700 |
---|---|---|
committer | Joe Wilm <joe@jwilm.com> | 2016-06-02 19:42:28 -0700 |
commit | 30ec14510935d46e7454863f9a4e63e53bf7728c (patch) | |
tree | 9501fe70ecf582e57903fbc061d3e6a0928f3f33 /src/term.rs | |
parent | 70b0423a31016798592fc0e96ce316cb3f1e9d46 (diff) | |
download | r-alacritty-30ec14510935d46e7454863f9a4e63e53bf7728c.tar.gz r-alacritty-30ec14510935d46e7454863f9a4e63e53bf7728c.tar.bz2 r-alacritty-30ec14510935d46e7454863f9a4e63e53bf7728c.zip |
Initial support for Terminal Emulation (woo!)
This patch introduces basic support for terminal emulation. Basic means
commands that don't use paging and are not full screen applications like
vim or tmux. Some paging applications are working properly, such as as
`git log`. Other pagers work reasonably well as long as the help menu is
not accessed.
There is now a central Rgb color type which is shared by the renderer,
terminal emulation, and the pty parser.
The parser no longer owns a Handler. Instead, a mutable reference to a
Handler is provided whenever advancing the parser. This resolved some
potential ownership issues (eg parser owning the `Term` type would've
been unworkable).
Diffstat (limited to 'src/term.rs')
-rw-r--r-- | src/term.rs | 350 |
1 files changed, 350 insertions, 0 deletions
diff --git a/src/term.rs b/src/term.rs new file mode 100644 index 00000000..28d7c220 --- /dev/null +++ b/src/term.rs @@ -0,0 +1,350 @@ +/// Exports the `Term` type which is a high-level API for the Grid +use std::sync::Arc; + +use ansi::{self, Attr, DebugHandler}; +use grid::Grid; +use tty; +use ::Rgb; + +/// tomorrow night bright +/// +/// because contrast +pub static COLORS: &'static [Rgb] = &[ + Rgb {r: 0x00, g: 0x00, b: 0x00}, // Black + Rgb {r: 0xd5, g: 0x4e, b: 0x53}, // Red + Rgb {r: 0xb9, g: 0xca, b: 0x4a}, // Green + Rgb {r: 0xe6, g: 0xc5, b: 0x47}, // Yellow + Rgb {r: 0x7a, g: 0xa6, b: 0xda}, // Blue + Rgb {r: 0xc3, g: 0x97, b: 0xd8}, // Magenta + Rgb {r: 0x70, g: 0xc0, b: 0xba}, // Cyan + Rgb {r: 0x42, g: 0x42, b: 0x42}, // White + Rgb {r: 0x66, g: 0x66, b: 0x66}, // Bright black + Rgb {r: 0xff, g: 0x33, b: 0x34}, // Bright red + Rgb {r: 0x9e, g: 0xc4, b: 0x00}, // Bright green + Rgb {r: 0xe7, g: 0xc5, b: 0x47}, // Bright yellow + Rgb {r: 0x7a, g: 0xa6, b: 0xda}, // Bright blue + Rgb {r: 0xb7, g: 0x7e, b: 0xe0}, // Bright magenta + Rgb {r: 0x54, g: 0xce, b: 0xd6}, // Bright cyan + Rgb {r: 0x2a, g: 0x2a, b: 0x2a}, // Bright white +]; + +pub const CURSOR_SHAPE: char = '█'; + +pub const DEFAULT_FG: Rgb = Rgb { r: 0xea, g: 0xea, b: 0xea}; +pub const DEFAULT_BG: Rgb = Rgb { r: 0, g: 0, b: 0}; +pub const TAB_SPACES: usize = 8; + +/// State for cursor +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct Cursor { + pub x: u16, + pub y: u16, +} + +impl Default for Cursor { + fn default() -> Cursor { + Cursor { x: 0, y: 0 } + } +} + +impl Cursor { + pub fn goto(&mut self, x: u16, y: u16) { + self.x = x; + self.y = y; + } + + pub fn advance(&mut self, rows: i64, cols: i64) { + self.x = (self.x as i64 + cols) as u16; + self.y = (self.y as i64 + rows) as u16; + } +} + +struct Mover<'a> { + cursor: &'a mut Cursor, +} + +pub struct Term { + /// The grid + grid: Grid, + + /// Alternate grid + alt_grid: Grid, + + /// Alt is active + alt: bool, + + /// Reference to the underlying tty + tty: tty::Tty, + + /// The cursor + cursor: Cursor, + + /// Alt cursor + alt_cursor: Cursor, + + /// Active foreground color + fg: Rgb, + + /// Active background color + bg: Rgb, + + /// Tabstops + tabs: Vec<bool> +} + +impl Term { + pub fn new(tty: tty::Tty, grid: Grid) -> Term { + + let mut tabs = (0..grid.cols()).map(|i| i % TAB_SPACES == 0) + .collect::<Vec<bool>>(); + tabs[0] = false; + + let alt = grid.clone(); + + Term { + grid: grid, + alt_grid: alt, + alt: false, + cursor: Cursor::default(), + alt_cursor: Cursor::default(), + fg: DEFAULT_FG, + bg: DEFAULT_BG, + tty: tty, + tabs: tabs, + } + } + + pub fn grid(&self) -> &Grid { + &self.grid + } + + pub fn swap_alt(&mut self) { + self.alt = !self.alt; + ::std::mem::swap(&mut self.grid, &mut self.alt_grid); + ::std::mem::swap(&mut self.cursor, &mut self.alt_cursor); + + if self.alt { + self.grid.clear(); + } + } + + pub fn resize(&mut self) { + unimplemented!(); + } + + #[inline] + pub fn cursor_x(&self) -> u16 { + self.cursor.x + } + + #[inline] + pub fn cursor_y(&self) -> u16 { + self.cursor.y + } + + /// Set character in current cursor position + fn set_char(&mut self, c: char) { + let cell = &mut self.grid[self.cursor]; + cell.c = c; + cell.fg = self.fg; + cell.bg = self.bg; + } + + /// Advance to next line + fn newline_c(&mut self, count: u16) { + // TODO handle scroll + self.cursor.x = 0; + self.cursor.y += 1; + } +} + +impl ansi::Handler for Term { + /// A character to be displayed + #[inline] + fn input(&mut self, c: char) { + self.set_char(c); + self.cursor.x += 1; + } + + fn goto(&mut self, x: i64, y: i64) { + println!("goto: x={}, y={}", x, y); + self.cursor.goto(x as u16, y as u16); + } + fn goto_row(&mut self, y: i64) { println!("goto_row: {}", y); } + fn goto_col(&mut self, x: i64) { println!("goto_col: {}", x); } + fn insert_blank(&mut self, num: i64) { println!("insert_blank: {}", num); } + + fn move_up(&mut self, rows: i64) { + println!("move_up: {}", rows); + self.cursor.advance(-rows, 0); + } + + fn move_down(&mut self, rows: i64) { + println!("move_down: {}", rows); + self.cursor.advance(rows, 0); + } + + fn move_forward(&mut self, cols: i64) { + println!("move_forward: {}", cols); + self.cursor.advance(0, cols); + } + + fn move_backward(&mut self, spaces: i64) { + println!("move_backward: {}", spaces); + self.cursor.advance(0, -spaces); + } + + fn identify_terminal(&mut self) { println!("identify_terminal"); } + fn move_down_and_cr(&mut self, rows: i64) { println!("move_down_and_cr: {}", rows); } + fn move_up_and_cr(&mut self, rows: i64) { println!("move_up_and_cr: {}", rows); } + fn put_tab(&mut self, mut count: i64) { + println!("put_tab: {}", count); + + let mut x = self.cursor_x(); + while x < self.grid.cols() as u16 && count != 0 { + count -= 1; + loop { + if x == self.grid.cols() as u16 || self.tabs[x as usize] { + break; + } + x += 1; + } + } + + self.cursor.x = x; + } + + /// Backspace `count` characters + #[inline] + fn backspace(&mut self, count: i64) { + self.cursor.x -= 1; + self.set_char(' '); + } + + /// Carriage return + #[inline] + fn carriage_return(&mut self) { + self.cursor.x = 0; + } + + /// Linefeed + #[inline] + fn linefeed(&mut self) { + println!("linefeed"); + // TODO handle scroll? not clear what parts of this the pty handle + if self.cursor_y() + 1 == self.grid.rows() as u16 { + self.grid.feed(); + self.clear_line(ansi::LineClearMode::Right); + } else { + self.cursor.y += 1; + } + } + + /// Set current position as a tabstop + fn bell(&mut self) { println!("bell"); } + fn substitute(&mut self) { println!("substitute"); } + fn newline(&mut self) { println!("newline"); } + fn set_horizontal_tabstop(&mut self) { println!("set_horizontal_tabstop"); } + fn scroll_up(&mut self, rows: i64) { println!("scroll_up: {}", rows); } + fn scroll_down(&mut self, rows: i64) { println!("scroll_down: {}", rows); } + fn insert_blank_lines(&mut self, count: i64) { println!("insert_blank_lines: {}", count); } + fn delete_lines(&mut self, count: i64) { println!("delete_lines: {}", count); } + fn erase_chars(&mut self, count: i64) { println!("erase_chars: {}", count); } + fn delete_chars(&mut self, count: i64) { println!("delete_chars: {}", count); } + fn move_backward_tabs(&mut self, count: i64) { println!("move_backward_tabs: {}", count); } + fn move_forward_tabs(&mut self, count: i64) { println!("move_forward_tabs: {}", count); } + fn save_cursor_position(&mut self) { println!("save_cursor_position"); } + fn restore_cursor_position(&mut self) { println!("restore_cursor_position"); } + fn clear_line(&mut self, mode: ansi::LineClearMode) { + println!("clear_line: {:?}", mode); + match mode { + ansi::LineClearMode::Right => { + let cols = self.grid.cols(); + let row = &mut self.grid[self.cursor.y as usize]; + let start = self.cursor.x as usize; + for col in start..cols { + row[col].c = ' '; + } + }, + _ => (), + } + } + fn clear_screen(&mut self, mode: ansi::ClearMode) { + println!("clear_screen: {:?}", mode); + match mode { + ansi::ClearMode::Below => { + let start = self.cursor_y() as usize; + let end = self.grid.rows(); + for i in start..end { + let row = &mut self.grid[i]; + for cell in row.iter_mut() { + cell.c = ' '; + } + } + }, + ansi::ClearMode::All => { + self.grid.clear(); + }, + _ => { + panic!("ansi::ClearMode::Above not implemented"); + } + } + } + fn clear_tabs(&mut self, mode: ansi::TabulationClearMode) { println!("clear_tabs: {:?}", mode); } + fn reset_state(&mut self) { println!("reset_state"); } + fn reverse_index(&mut self) { + println!("reverse_index"); + // if cursor is at the top + if self.cursor.y == 0 { + self.grid.unfeed(); + } else { + // can't wait for nonlexical lifetimes.. omg borrowck + let x = self.cursor.x; + let y = self.cursor.y; + self.cursor.goto(x, y - 1); + } + } + + /// set a terminal attribute + fn terminal_attribute(&mut self, attr: Attr) { + match attr { + Attr::DefaultForeground => { + self.fg = DEFAULT_FG; + }, + Attr::DefaultBackground => { + self.bg = DEFAULT_BG; + }, + Attr::Foreground(named_color) => { + self.fg = COLORS[named_color as usize]; + }, + Attr::Background(named_color) => { + self.bg = COLORS[named_color as usize]; + }, + Attr::Reset => { + self.fg = DEFAULT_FG; + self.bg = DEFAULT_BG; + } + _ => { + println!("Term got unhandled attr: {:?}", attr); + } + } + } + + fn set_mode(&mut self, mode: ansi::Mode) { + println!("set_mode: {:?}", mode); + match mode { + ansi::Mode::SwapScreenAndSetRestoreCursor => { + self.swap_alt(); + } + } + } + + fn unset_mode(&mut self, mode: ansi::Mode) { + println!("unset_mode: {:?}", mode); + match mode { + ansi::Mode::SwapScreenAndSetRestoreCursor => { + self.swap_alt(); + } + } + } +} |