aboutsummaryrefslogtreecommitdiff
path: root/src/term.rs
diff options
context:
space:
mode:
authorJoe Wilm <joe@jwilm.com>2016-05-30 20:44:37 -0700
committerJoe Wilm <joe@jwilm.com>2016-06-02 19:42:28 -0700
commit30ec14510935d46e7454863f9a4e63e53bf7728c (patch)
tree9501fe70ecf582e57903fbc061d3e6a0928f3f33 /src/term.rs
parent70b0423a31016798592fc0e96ce316cb3f1e9d46 (diff)
downloadr-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.rs350
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();
+ }
+ }
+ }
+}