diff options
Diffstat (limited to 'src/term.rs')
-rw-r--r-- | src/term.rs | 350 |
1 files changed, 200 insertions, 150 deletions
diff --git a/src/term.rs b/src/term.rs index 699df8de..9c8cff30 100644 --- a/src/term.rs +++ b/src/term.rs @@ -14,9 +14,10 @@ // //! Exports the `Term` type which is a high-level API for the Grid use std::ops::Range; +use std::fmt; use ansi::{self, Attr}; -use grid::{self, Grid, CellFlags, ClearRegion}; +use grid::{Grid, ClearRegion}; use tty; use ::Rgb; @@ -31,6 +32,49 @@ fn limit<T: PartialOrd>(val: T, min: T, max: T) -> T { } } +pub mod cell { + use super::{DEFAULT_FG, DEFAULT_BG}; + use ::Rgb; + + bitflags! { + pub flags Flags: u32 { + const INVERSE = 0b00000001, + const BOLD = 0b00000010, + const ITALIC = 0b00000100, + const UNDERLINE = 0b00001000, + } + } + + #[derive(Clone, Debug)] + pub struct Cell { + pub c: char, + pub fg: Rgb, + pub bg: Rgb, + pub flags: Flags, + } + + impl Cell { + pub fn new(c: char) -> Cell { + Cell { + c: c.into(), + bg: Default::default(), + fg: Default::default(), + flags: Flags::empty(), + } + } + + pub fn reset(&mut self) { + self.c = ' '; + self.flags = Flags::empty(); + + self.bg = DEFAULT_BG; + self.fg = DEFAULT_FG; + } + } +} + +pub use self::cell::Cell; + /// tomorrow night bright /// /// because contrast @@ -78,37 +122,31 @@ 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, -} +use grid::index::{Cursor, Column, Line}; -impl Default for Cursor { - fn default() -> Cursor { - Cursor { x: 0, y: 0 } - } +trait CursorExt { + fn goto(&mut self, Line, Column); + fn advance(&mut self, Line, Column); } -impl Cursor { - pub fn goto(&mut self, x: u16, y: u16) { - self.x = x; - self.y = y; +impl CursorExt for Cursor { + fn goto(&mut self, line: Line, col: Column) { + self.col = col; + self.line = line; } - 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; + fn advance(&mut self, lines: Line, cols: Column) { + self.col = self.col + cols; + self.line = self.line + lines; } } pub struct Term { /// The grid - grid: Grid, + grid: Grid<Cell>, /// Alternate grid - alt_grid: Grid, + alt_grid: Grid<Cell>, /// Alt is active alt: bool, @@ -132,13 +170,13 @@ pub struct Term { tabs: Vec<bool>, /// Cell attributes - attr: grid::CellFlags, + attr: cell::Flags, /// Mode flags mode: TermMode, /// Scroll region - scroll_region: Range<usize>, + scroll_region: Range<Line>, /// Size size_info: SizeInfo, @@ -162,13 +200,13 @@ pub struct SizeInfo { impl SizeInfo { #[inline] - pub fn rows(&self) -> usize { - (self.height / self.cell_height) as usize + pub fn lines(&self) -> Line { + Line((self.height / self.cell_height) as usize) } #[inline] - pub fn cols(&self) -> usize { - (self.width / self.cell_width) as usize + pub fn cols(&self) -> Column { + Column((self.width / self.cell_width) as usize) } } @@ -182,21 +220,21 @@ impl Term { }; let num_cols = size.cols(); - let num_rows = size.rows(); + let num_lines = size.lines(); - println!("num_cols, num_rows = {}, {}", num_cols, num_rows); + println!("num_cols, num_lines = {}, {}", num_cols, num_lines); - let grid = Grid::new(num_rows, num_cols); + let grid = Grid::new(num_lines, num_cols, &Cell::new(' ')); - let tty = tty::new(num_rows as u8, num_cols as u8); - tty.resize(num_rows, num_cols, size.width as usize, size.height as usize); + let tty = tty::new(*num_lines as u8, *num_cols as u8); + tty.resize(*num_lines as usize, *num_cols as usize, size.width as usize, size.height as usize); - let mut tabs = (0..grid.num_cols()).map(|i| i % TAB_SPACES == 0) - .collect::<Vec<bool>>(); + let mut tabs = (Column(0)..grid.num_cols()).map(|i| (*i as usize) % TAB_SPACES == 0) + .collect::<Vec<bool>>(); tabs[0] = false; let alt = grid.clone(); - let scroll_region = 0..grid.num_rows(); + let scroll_region = Line(0)..grid.num_lines(); Term { grid: grid, @@ -208,7 +246,7 @@ impl Term { bg: DEFAULT_BG, tty: tty, tabs: tabs, - attr: CellFlags::empty(), + attr: cell::Flags::empty(), mode: Default::default(), scroll_region: scroll_region, size_info: size @@ -225,52 +263,51 @@ impl Term { }; let old_cols = self.size_info.cols(); - let old_rows = self.size_info.rows(); + let old_lines = self.size_info.lines(); let num_cols = size.cols(); - let num_rows = size.rows(); + let num_lines = size.lines(); self.size_info = size; - if old_cols == num_cols && old_rows == num_rows { + if old_cols == num_cols && old_lines == num_lines { return; } // Scroll up to keep cursor and as much context as possible in grid. This only runs when the - // rows decreases. - self.scroll_region = 0..self.grid.num_rows(); - // XXX why is +1 required? - let row_diff = (self.cursor_y() as isize - num_rows as isize) + 1; - if row_diff > 0 { - self.scroll(row_diff); - self.cursor.advance(-row_diff as i64, 0); + // lines decreases. + self.scroll_region = Line(0)..self.grid.num_lines(); + + // Scroll up to keep cursor in terminal + if self.cursor.line >= num_lines { + let lines = self.cursor.line - num_lines + 1; + self.scroll(lines, ScrollDirection::Down); + self.cursor.line -= lines; } - println!("num_cols, num_rows = {}, {}", num_cols, num_rows); + println!("num_cols, num_lines = {}, {}", num_cols, num_lines); // Resize grids to new size - self.grid.resize(num_rows, num_cols); - self.alt_grid.resize(num_rows, num_cols); + self.grid.resize(num_lines, num_cols, &Cell::new(' ')); + self.alt_grid.resize(num_lines, num_cols, &Cell::new(' ')); // Ensure cursor is in-bounds - self.cursor.y = limit(self.cursor.y, 0, num_rows as u16); - self.cursor.x = limit(self.cursor.x, 0, num_cols as u16); + self.cursor.line = limit(self.cursor.line, Line(0), num_lines); + self.cursor.col = limit(self.cursor.col, Column(0), num_cols); // Recreate tabs list - self.tabs = (0..self.grid.num_cols()).map(|i| i % TAB_SPACES == 0) - .collect::<Vec<bool>>(); + self.tabs = (Column(0)..self.grid.num_cols()).map(|i| (*i as usize) % TAB_SPACES == 0) + .collect::<Vec<bool>>(); // Make sure bottom of terminal is clear - if row_diff > 0 { - self.grid.clear_region((self.cursor.y as usize)..); - self.alt_grid.clear_region((self.cursor.y as usize)..); - } + self.grid.clear_region((self.cursor.line).., |c| c.reset()); + self.alt_grid.clear_region((self.cursor.line).., |c| c.reset()); // Reset scrolling region to new size - self.scroll_region = 0..self.grid.num_rows(); + self.scroll_region = Line(0)..self.grid.num_lines(); // Inform tty of new dimensions - self.tty.resize(num_rows, - num_cols, + self.tty.resize(*num_lines as _, + *num_cols as _, self.size_info.width as usize, self.size_info.height as usize); @@ -286,7 +323,8 @@ impl Term { &self.size_info } - pub fn grid(&self) -> &Grid { + #[inline] + pub fn grid(&self) -> &Grid<Cell> { &self.grid } @@ -301,38 +339,28 @@ impl Term { ::std::mem::swap(&mut self.cursor, &mut self.alt_cursor); if self.alt { - self.grid.clear(); + self.grid.clear(|c| c.reset()); } } #[inline] - pub fn cursor_x(&self) -> u16 { - self.cursor.x - } - - #[inline] - pub fn cursor_y(&self) -> u16 { - self.cursor.y - } - - #[inline] - pub fn cursor(&self) -> Cursor { - self.cursor + pub fn cursor(&self) -> &Cursor { + &self.cursor } /// Set character in current cursor position fn set_char(&mut self, c: char) { - if self.cursor.x == self.grid.num_cols() as u16 { + if self.cursor.col == self.grid.num_cols() { println!("wrapping"); - self.cursor.y += 1; - self.cursor.x = 0; + self.cursor.line += 1; + self.cursor.col = Column(0); } - if self.cursor.y == self.grid.num_rows() as u16 { + if self.cursor.line == self.grid.num_lines() { panic!("cursor fell off grid"); } - let cell = &mut self.grid[self.cursor]; + let cell = &mut self.grid[&self.cursor]; cell.c = c; cell.fg = self.fg; cell.bg = self.bg; @@ -340,17 +368,44 @@ impl Term { } /// Convenience function for scrolling - fn scroll(&mut self, count: isize) { - println!("[TERM] scrolling {} lines", count); - self.grid.scroll(self.scroll_region.clone(), count); - if count > 0 { - // Scrolled down, so need to clear from bottom - let start = self.scroll_region.end - (count as usize); - self.grid.clear_region(start..self.scroll_region.end); - } else { - // Scrolled up, clear from top - let end = self.scroll_region.start + ((-count) as usize); - self.grid.clear_region(self.scroll_region.start..end); + fn scroll(&mut self, lines: Line, direction: ScrollDirection) { + println!("[TERM] scrolling {} {} lines", direction, lines); + match direction { + ScrollDirection::Down => { + // Scrolled down, so need to clear from bottom + self.grid.scroll(self.scroll_region.clone(), *lines as isize); + let start = self.scroll_region.end - lines; + self.grid.clear_region(start..self.scroll_region.end, |c| c.reset()); + }, + ScrollDirection::Up => { + // Scrolled up, clear from top + self.grid.scroll(self.scroll_region.clone(), -(*lines as isize)); + let end = self.scroll_region.start + lines; + self.grid.clear_region(self.scroll_region.start..end, |c| c.reset()); + } + } + } +} + +/// Which direction to scroll +#[derive(Debug)] +enum ScrollDirection { + /// Scroll up + /// + /// Lines move down + Up, + + /// Scroll down + /// + /// Lines move up + Down, +} + +impl fmt::Display for ScrollDirection { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ScrollDirection::Up => write!(f, "up"), + ScrollDirection::Down => write!(f, "down"), } } } @@ -358,12 +413,12 @@ impl Term { impl ansi::TermInfo for Term { #[inline] fn rows(&self) -> usize { - self.grid.num_rows() + *self.grid.num_lines() as usize } #[inline] fn cols(&self) -> usize { - self.grid.num_cols() + *self.grid.num_cols() as usize } } @@ -372,90 +427,90 @@ impl ansi::Handler for Term { #[inline] fn input(&mut self, c: char) { self.set_char(c); - self.cursor.x += 1; + self.cursor.col += 1; } + #[inline] fn goto(&mut self, x: i64, y: i64) { println!("goto: x={}, y={}", x, y); - self.cursor.goto(x as u16, y as u16); + self.cursor.goto(Line(y as usize), Column(x as usize)); } - fn goto_row(&mut self, y: i64) { - println!("goto_row: {}", y); - let x = self.cursor_x(); - self.cursor.goto(x, y as u16); + + fn goto_row(&mut self, row: i64) { + println!("goto_row: {}", row); + self.cursor.line = Line(row as usize); } - fn goto_col(&mut self, x: i64) { - println!("goto_col: {}", x); - let y = self.cursor_y(); - self.cursor.goto(x as u16, y); + fn goto_col(&mut self, col: i64) { + println!("goto_col: {}", col); + self.cursor.col = Column(col as usize); } 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); + self.cursor.line -= Line(rows as usize); } fn move_down(&mut self, rows: i64) { println!("move_down: {}", rows); - self.cursor.advance(rows, 0); + self.cursor.line += Line(rows as usize); } fn move_forward(&mut self, cols: i64) { println!("move_forward: {}", cols); - self.cursor.advance(0, cols); + self.cursor.col += Column(cols as usize); } fn move_backward(&mut self, spaces: i64) { println!("move_backward: {}", spaces); - self.cursor.advance(0, -spaces); + self.cursor.col -= Column(spaces as usize); } 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.num_cols() as u16 && count != 0 { + let mut col = self.cursor.col; + while col < self.grid.num_cols() && count != 0 { count -= 1; loop { - if x == self.grid.num_cols() as u16 || self.tabs[x as usize] { + if col == self.grid.num_cols() || self.tabs[*col as usize] { break; } - x += 1; + col += 1; } } - self.cursor.x = x; + self.cursor.col = col; } /// Backspace `count` characters #[inline] fn backspace(&mut self) { println!("backspace"); - self.cursor.x -= 1; + self.cursor.col -= 1; } /// Carriage return #[inline] fn carriage_return(&mut self) { println!("carriage_return"); - self.cursor.x = 0; + self.cursor.col = Column(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.scroll_region.end as u16 { - self.scroll(1); + if self.cursor.line + 1 >= self.scroll_region.end { + self.scroll(Line(1), ScrollDirection::Down); self.clear_line(ansi::LineClearMode::Right); } else { - self.cursor.y += 1; + self.cursor.line += 1; } } @@ -466,31 +521,30 @@ impl ansi::Handler for Term { fn set_horizontal_tabstop(&mut self) { println!("set_horizontal_tabstop"); } fn scroll_up(&mut self, rows: i64) { println!("scroll_up: {}", rows); - self.scroll(rows as isize); + self.scroll(Line(rows as usize), ScrollDirection::Up); } fn scroll_down(&mut self, rows: i64) { println!("scroll_down: {}", rows); - self.scroll(-rows as isize); + self.scroll(Line(rows as usize), ScrollDirection::Down); } fn insert_blank_lines(&mut self, count: i64) { println!("insert_blank_lines: {}", count); - if self.scroll_region.contains(self.cursor_y() as usize) { - self.scroll(-count as isize); + if self.scroll_region.contains(self.cursor.line) { + self.scroll(Line(count as usize), ScrollDirection::Down); } } fn delete_lines(&mut self, count: i64) { - if self.scroll_region.contains(self.cursor_y() as usize) { - self.scroll(count as isize); + if self.scroll_region.contains(self.cursor.line) { + self.scroll(Line(count as usize), ScrollDirection::Up); } } fn erase_chars(&mut self, count: i64) { println!("erase_chars: {}", count); - let row_index = self.cursor.y as usize; - let col_index = self.cursor.x as usize; + let col_index = self.cursor.col; let count = count as usize; - let row = &mut self.grid[row_index]; - for c in &mut row[col_index..(count + col_index)] { + let row = &mut self.grid[self.cursor.line]; + for c in &mut row[self.cursor.col..(col_index + count)] { c.reset(); } } @@ -503,9 +557,8 @@ impl ansi::Handler for Term { println!("clear_line: {:?}", mode); match mode { ansi::LineClearMode::Right => { - let row = &mut self.grid[self.cursor.y as usize]; - let start = self.cursor.x as usize; - for cell in &mut row[start..] { + let row = &mut self.grid[self.cursor.line]; + for cell in &mut row[self.cursor.col..] { cell.reset(); } }, @@ -516,17 +569,17 @@ impl ansi::Handler for Term { println!("clear_screen: {:?}", mode); match mode { ansi::ClearMode::Below => { - let start = self.cursor_y() as usize; - let end = self.grid.num_rows(); - for i in start..end { - let row = &mut self.grid[i]; + let start = self.cursor.line; + let end = self.grid.num_lines(); + + for row in &mut self.grid[start..end] { for cell in row { cell.reset(); } } }, ansi::ClearMode::All => { - self.grid.clear(); + self.grid.clear(|c| c.reset()); }, _ => { panic!("ansi::ClearMode::Above not implemented"); @@ -538,13 +591,10 @@ impl ansi::Handler for Term { fn reverse_index(&mut self) { println!("reverse_index"); // if cursor is at the top - if self.cursor.y == 0 { - self.scroll(-1); + if self.cursor.col == Column(0) { + self.scroll(Line(1), ScrollDirection::Up); } 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); + self.cursor.col -= 1; } } @@ -572,16 +622,16 @@ impl ansi::Handler for Term { Attr::Reset => { self.fg = DEFAULT_FG; self.bg = DEFAULT_BG; - self.attr = CellFlags::empty(); + self.attr = cell::Flags::empty(); }, - Attr::Reverse => self.attr.insert(grid::INVERSE), - Attr::CancelReverse => self.attr.remove(grid::INVERSE), - Attr::Bold => self.attr.insert(grid::BOLD), - Attr::CancelBoldDim => self.attr.remove(grid::BOLD), - Attr::Italic => self.attr.insert(grid::ITALIC), - Attr::CancelItalic => self.attr.remove(grid::ITALIC), - Attr::Underscore => self.attr.insert(grid::UNDERLINE), - Attr::CancelUnderline => self.attr.remove(grid::UNDERLINE), + Attr::Reverse => self.attr.insert(cell::INVERSE), + Attr::CancelReverse => self.attr.remove(cell::INVERSE), + Attr::Bold => self.attr.insert(cell::BOLD), + Attr::CancelBoldDim => self.attr.remove(cell::BOLD), + Attr::Italic => self.attr.insert(cell::ITALIC), + Attr::CancelItalic => self.attr.remove(cell::ITALIC), + Attr::Underscore => self.attr.insert(cell::UNDERLINE), + Attr::CancelUnderline => self.attr.remove(cell::UNDERLINE), _ => { println!("Term got unhandled attr: {:?}", attr); } @@ -615,6 +665,6 @@ impl ansi::Handler for Term { fn set_scrolling_region(&mut self, top: i64, bot: i64) { println!("set scroll region: {:?} - {:?}", top, bot); // 1 is added to bottom for inclusive range - self.scroll_region = (top as usize)..((bot as usize) + 1); + self.scroll_region = Line(top as usize)..Line((bot as usize) + 1); } } |