2016-06-30 03:56:12 +00:00
|
|
|
// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
//
|
|
|
|
//! Exports the `Term` type which is a high-level API for the Grid
|
2016-07-04 21:46:25 +00:00
|
|
|
use std::ops::{Deref, Range};
|
2016-09-15 15:49:55 +00:00
|
|
|
use std::ptr;
|
2016-11-25 20:03:11 +00:00
|
|
|
use std::cmp;
|
2016-12-04 23:48:30 +00:00
|
|
|
use std::io;
|
2016-05-31 03:44:37 +00:00
|
|
|
|
2016-07-30 03:16:13 +00:00
|
|
|
use ansi::{self, Attr, Handler};
|
2016-07-04 00:00:00 +00:00
|
|
|
use grid::{Grid, ClearRegion};
|
2016-07-04 04:05:28 +00:00
|
|
|
use index::{Cursor, Column, Line};
|
2016-12-04 19:14:27 +00:00
|
|
|
use ansi::{Color, NamedColor};
|
2016-05-31 03:44:37 +00:00
|
|
|
|
2016-11-28 22:30:08 +00:00
|
|
|
pub mod cell;
|
|
|
|
pub use self::cell::Cell;
|
|
|
|
|
2016-11-28 22:13:11 +00:00
|
|
|
/// Iterator that yields cells needing render
|
|
|
|
///
|
|
|
|
/// Yields cells that require work to be displayed (that is, not a an empty
|
|
|
|
/// background cell). Additionally, this manages some state of the grid only
|
|
|
|
/// relevant for rendering like temporarily changing the cell with the cursor.
|
2016-07-04 21:46:25 +00:00
|
|
|
///
|
|
|
|
/// This manages the cursor during a render. The cursor location is inverted to
|
|
|
|
/// draw it, and reverted after drawing to maintain state.
|
2016-11-28 22:13:11 +00:00
|
|
|
pub struct RenderableCellsIter<'a> {
|
|
|
|
grid: &'a mut Grid<Cell>,
|
2016-07-04 21:46:25 +00:00
|
|
|
cursor: &'a Cursor,
|
|
|
|
mode: TermMode,
|
2016-11-28 22:13:11 +00:00
|
|
|
line: Line,
|
|
|
|
column: Column,
|
2016-07-04 21:46:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-11-28 22:13:11 +00:00
|
|
|
impl<'a> RenderableCellsIter<'a> {
|
|
|
|
/// Create the renderable cells iterator
|
|
|
|
///
|
|
|
|
/// The cursor and terminal mode are required for properly displaying the
|
|
|
|
/// cursor.
|
|
|
|
fn new<'b>(
|
|
|
|
grid: &'b mut Grid<Cell>,
|
|
|
|
cursor: &'b Cursor,
|
|
|
|
mode: TermMode
|
|
|
|
) -> RenderableCellsIter<'b> {
|
|
|
|
RenderableCellsIter {
|
|
|
|
grid: grid,
|
2016-07-04 21:46:25 +00:00
|
|
|
cursor: cursor,
|
|
|
|
mode: mode,
|
2016-11-28 22:13:11 +00:00
|
|
|
line: Line(0),
|
|
|
|
column: Column(0),
|
|
|
|
}.initialize()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn initialize(self) -> Self {
|
|
|
|
if self.cursor_is_visible() {
|
|
|
|
self.grid[self.cursor].swap_fg_and_bg();
|
2016-07-04 21:46:25 +00:00
|
|
|
}
|
2016-11-28 22:13:11 +00:00
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Check if the cursor should be rendered.
|
|
|
|
#[inline]
|
|
|
|
fn cursor_is_visible(&self) -> bool {
|
|
|
|
self.mode.contains(mode::SHOW_CURSOR) && self.grid.contains(self.cursor)
|
2016-07-04 21:46:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-28 22:13:11 +00:00
|
|
|
impl<'a> Drop for RenderableCellsIter<'a> {
|
|
|
|
/// Resets temporary render state on the grid
|
2016-07-04 21:46:25 +00:00
|
|
|
fn drop(&mut self) {
|
2016-11-28 22:13:11 +00:00
|
|
|
if self.cursor_is_visible() {
|
|
|
|
self.grid[self.cursor].swap_fg_and_bg();
|
2016-07-04 21:46:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-28 22:13:11 +00:00
|
|
|
pub struct IndexedCell {
|
|
|
|
pub line: Line,
|
|
|
|
pub column: Column,
|
|
|
|
pub inner: Cell
|
|
|
|
}
|
2016-07-04 21:46:25 +00:00
|
|
|
|
2016-11-28 22:13:11 +00:00
|
|
|
impl Deref for IndexedCell {
|
|
|
|
type Target = Cell;
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
fn deref(&self) -> &Cell {
|
|
|
|
&self.inner
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Iterator for RenderableCellsIter<'a> {
|
|
|
|
type Item = IndexedCell;
|
|
|
|
|
|
|
|
/// Gets the next renderable cell
|
|
|
|
///
|
|
|
|
/// Skips empty (background) cells and applies any flags to the cell state
|
|
|
|
/// (eg. invert fg and bg colors).
|
|
|
|
#[inline(always)]
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
while self.line < self.grid.num_lines() {
|
|
|
|
while self.column < self.grid.num_cols() {
|
|
|
|
// Grab current state for this iteration
|
|
|
|
let line = self.line;
|
|
|
|
let column = self.column;
|
|
|
|
let cell = &self.grid[line][column];
|
|
|
|
|
|
|
|
// Update state for next iteration
|
|
|
|
self.column += 1;
|
|
|
|
|
|
|
|
// Skip empty cells
|
|
|
|
if cell.is_empty() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// fg, bg are dependent on INVERSE flag
|
|
|
|
let (fg, bg) = if cell.flags.contains(cell::INVERSE) {
|
|
|
|
(&cell.bg, &cell.fg)
|
|
|
|
} else {
|
|
|
|
(&cell.fg, &cell.bg)
|
|
|
|
};
|
|
|
|
|
|
|
|
return Some(IndexedCell {
|
|
|
|
line: line,
|
|
|
|
column: column,
|
|
|
|
inner: Cell {
|
|
|
|
flags: cell.flags,
|
|
|
|
c: cell.c,
|
|
|
|
fg: *fg,
|
|
|
|
bg: *bg,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
self.column = Column(0);
|
|
|
|
self.line += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
2016-07-04 21:46:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-29 17:21:02 +00:00
|
|
|
/// coerce val to be between min and max
|
2016-11-25 20:03:11 +00:00
|
|
|
#[inline]
|
|
|
|
fn limit<T: PartialOrd + Ord>(val: T, min: T, max: T) -> T {
|
|
|
|
cmp::min(cmp::max(min, val), max)
|
2016-06-29 17:21:02 +00:00
|
|
|
}
|
|
|
|
|
2016-06-08 04:17:48 +00:00
|
|
|
pub mod mode {
|
|
|
|
bitflags! {
|
2016-06-23 16:42:00 +00:00
|
|
|
pub flags TermMode: u8 {
|
2016-11-24 04:25:37 +00:00
|
|
|
const SHOW_CURSOR = 0b00000001,
|
|
|
|
const APP_CURSOR = 0b00000010,
|
|
|
|
const APP_KEYPAD = 0b00000100,
|
|
|
|
const MOUSE_REPORT_CLICK = 0b00001000,
|
2016-11-28 22:39:37 +00:00
|
|
|
const BRACKETED_PASTE = 0b00010000,
|
2016-11-24 04:25:37 +00:00
|
|
|
const ANY = 0b11111111,
|
|
|
|
const NONE = 0b00000000,
|
2016-06-23 16:42:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for TermMode {
|
|
|
|
fn default() -> TermMode {
|
|
|
|
SHOW_CURSOR
|
2016-06-08 04:17:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub use self::mode::TermMode;
|
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
pub const TAB_SPACES: usize = 8;
|
|
|
|
|
|
|
|
pub struct Term {
|
|
|
|
/// The grid
|
2016-07-04 00:00:00 +00:00
|
|
|
grid: Grid<Cell>,
|
2016-05-31 03:44:37 +00:00
|
|
|
|
|
|
|
/// Alternate grid
|
2016-07-04 00:00:00 +00:00
|
|
|
alt_grid: Grid<Cell>,
|
2016-05-31 03:44:37 +00:00
|
|
|
|
|
|
|
/// Alt is active
|
|
|
|
alt: bool,
|
|
|
|
|
|
|
|
/// The cursor
|
|
|
|
cursor: Cursor,
|
|
|
|
|
|
|
|
/// Alt cursor
|
|
|
|
alt_cursor: Cursor,
|
|
|
|
|
|
|
|
/// Tabstops
|
2016-06-06 23:54:15 +00:00
|
|
|
tabs: Vec<bool>,
|
|
|
|
|
2016-06-08 04:17:48 +00:00
|
|
|
/// Mode flags
|
|
|
|
mode: TermMode,
|
2016-06-08 17:39:49 +00:00
|
|
|
|
|
|
|
/// Scroll region
|
2016-07-04 00:00:00 +00:00
|
|
|
scroll_region: Range<Line>,
|
2016-06-28 16:18:54 +00:00
|
|
|
|
|
|
|
/// Size
|
|
|
|
size_info: SizeInfo,
|
2016-07-30 03:16:13 +00:00
|
|
|
|
|
|
|
/// Template cell
|
2016-09-01 17:24:20 +00:00
|
|
|
template_cell: Cell,
|
|
|
|
|
2016-10-15 22:56:27 +00:00
|
|
|
/// Empty cell
|
|
|
|
empty_cell: Cell,
|
|
|
|
|
2016-09-01 17:24:20 +00:00
|
|
|
pub dirty: bool,
|
2016-06-28 16:18:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Terminal size info
|
2016-11-20 00:16:20 +00:00
|
|
|
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
2016-06-28 16:18:54 +00:00
|
|
|
pub struct SizeInfo {
|
|
|
|
/// Terminal window width
|
|
|
|
pub width: f32,
|
|
|
|
|
|
|
|
/// Terminal window height
|
|
|
|
pub height: f32,
|
|
|
|
|
|
|
|
/// Width of individual cell
|
|
|
|
pub cell_width: f32,
|
|
|
|
|
|
|
|
/// Height of individual cell
|
|
|
|
pub cell_height: f32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SizeInfo {
|
|
|
|
#[inline]
|
2016-07-04 00:00:00 +00:00
|
|
|
pub fn lines(&self) -> Line {
|
|
|
|
Line((self.height / self.cell_height) as usize)
|
2016-06-28 16:18:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 00:00:00 +00:00
|
|
|
pub fn cols(&self) -> Column {
|
|
|
|
Column((self.width / self.cell_width) as usize)
|
2016-06-28 16:18:54 +00:00
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Term {
|
2016-11-28 22:27:52 +00:00
|
|
|
pub fn new(size: SizeInfo) -> Term {
|
2016-10-23 22:37:06 +00:00
|
|
|
let template = Cell::new(
|
|
|
|
' ',
|
2016-12-04 19:14:27 +00:00
|
|
|
Color::Named(NamedColor::Foreground),
|
|
|
|
Color::Named(NamedColor::Background)
|
2016-10-23 22:37:06 +00:00
|
|
|
);
|
2016-07-30 03:16:13 +00:00
|
|
|
|
2016-06-28 16:18:54 +00:00
|
|
|
let num_cols = size.cols();
|
2016-07-04 00:00:00 +00:00
|
|
|
let num_lines = size.lines();
|
2016-06-28 16:18:54 +00:00
|
|
|
|
2016-10-23 22:37:06 +00:00
|
|
|
let grid = Grid::new(num_lines, num_cols, &template);
|
2016-06-28 16:18:54 +00:00
|
|
|
|
2016-07-30 03:16:13 +00:00
|
|
|
let mut tabs = (Column(0)..grid.num_cols())
|
|
|
|
.map(|i| (*i as usize) % TAB_SPACES == 0)
|
|
|
|
.collect::<Vec<bool>>();
|
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
tabs[0] = false;
|
|
|
|
|
|
|
|
let alt = grid.clone();
|
2016-07-04 00:00:00 +00:00
|
|
|
let scroll_region = Line(0)..grid.num_lines();
|
2016-05-31 03:44:37 +00:00
|
|
|
|
|
|
|
Term {
|
2016-09-01 17:24:20 +00:00
|
|
|
dirty: true,
|
2016-05-31 03:44:37 +00:00
|
|
|
grid: grid,
|
|
|
|
alt_grid: alt,
|
|
|
|
alt: false,
|
|
|
|
cursor: Cursor::default(),
|
|
|
|
alt_cursor: Cursor::default(),
|
|
|
|
tabs: tabs,
|
2016-06-23 16:42:00 +00:00
|
|
|
mode: Default::default(),
|
2016-06-08 17:39:49 +00:00
|
|
|
scroll_region: scroll_region,
|
2016-07-30 03:16:13 +00:00
|
|
|
size_info: size,
|
2016-10-23 22:37:06 +00:00
|
|
|
template_cell: template.clone(),
|
2016-10-15 22:56:27 +00:00
|
|
|
empty_cell: template,
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-24 04:25:37 +00:00
|
|
|
/// Convert the given pixel values to a grid coordinate
|
|
|
|
///
|
|
|
|
/// The mouse coordinates are expected to be relative to the top left. The
|
|
|
|
/// line and column returned are also relative to the top left.
|
|
|
|
///
|
|
|
|
/// Returns None if the coordinates are outside the screen
|
|
|
|
pub fn pixels_to_coords(&self, x: usize, y: usize) -> Option<(Line, Column)> {
|
|
|
|
let size = self.size_info();
|
|
|
|
if x > size.width as usize || y > size.height as usize {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
let col = Column(x / (size.cell_width as usize));
|
|
|
|
let line = Line(y / (size.cell_height as usize));
|
|
|
|
|
|
|
|
Some((line, col))
|
|
|
|
}
|
|
|
|
|
2016-11-28 22:27:52 +00:00
|
|
|
/// Access to the raw grid data structure
|
|
|
|
///
|
|
|
|
/// This is a bit of a hack; when the window is closed, the event processor
|
|
|
|
/// serializes the grid state to a file.
|
2016-11-20 00:16:20 +00:00
|
|
|
pub fn grid(&self) -> &Grid<Cell> {
|
|
|
|
&self.grid
|
|
|
|
}
|
|
|
|
|
2016-11-28 22:27:52 +00:00
|
|
|
/// Iterate over the *renderable* cells in the terminal
|
|
|
|
///
|
|
|
|
/// A renderable cell is any cell which has content other than the default
|
|
|
|
/// background color. Cells with an alternate background color are
|
|
|
|
/// considered renderable as are cells with any text content.
|
2016-11-28 22:13:11 +00:00
|
|
|
pub fn renderable_cells<'a>(&'a mut self) -> RenderableCellsIter<'a> {
|
|
|
|
RenderableCellsIter::new(&mut self.grid, &self.cursor, self.mode)
|
2016-07-04 21:46:25 +00:00
|
|
|
}
|
|
|
|
|
2016-06-29 17:21:02 +00:00
|
|
|
/// Resize terminal to new dimensions
|
|
|
|
pub fn resize(&mut self, width: f32, height: f32) {
|
|
|
|
let size = SizeInfo {
|
|
|
|
width: width,
|
|
|
|
height: height,
|
|
|
|
cell_width: self.size_info.cell_width,
|
|
|
|
cell_height: self.size_info.cell_height,
|
|
|
|
};
|
|
|
|
|
|
|
|
let old_cols = self.size_info.cols();
|
2016-07-04 00:00:00 +00:00
|
|
|
let old_lines = self.size_info.lines();
|
2016-06-29 17:21:02 +00:00
|
|
|
let num_cols = size.cols();
|
2016-07-04 00:00:00 +00:00
|
|
|
let num_lines = size.lines();
|
2016-06-29 17:21:02 +00:00
|
|
|
|
|
|
|
self.size_info = size;
|
|
|
|
|
2016-07-04 00:00:00 +00:00
|
|
|
if old_cols == num_cols && old_lines == num_lines {
|
2016-06-29 17:21:02 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scroll up to keep cursor and as much context as possible in grid. This only runs when the
|
2016-07-04 00:00:00 +00:00
|
|
|
// 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;
|
2016-08-20 03:35:13 +00:00
|
|
|
self.scroll_up(lines);
|
2016-07-04 00:00:00 +00:00
|
|
|
self.cursor.line -= lines;
|
2016-06-29 17:21:02 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:00:00 +00:00
|
|
|
println!("num_cols, num_lines = {}, {}", num_cols, num_lines);
|
2016-06-29 17:21:02 +00:00
|
|
|
|
|
|
|
// Resize grids to new size
|
2016-10-23 22:37:06 +00:00
|
|
|
let template = self.template_cell.clone();
|
|
|
|
self.grid.resize(num_lines, num_cols, &template);
|
|
|
|
self.alt_grid.resize(num_lines, num_cols, &template);
|
2016-06-29 17:21:02 +00:00
|
|
|
|
|
|
|
// Ensure cursor is in-bounds
|
2016-07-04 00:00:00 +00:00
|
|
|
self.cursor.line = limit(self.cursor.line, Line(0), num_lines);
|
|
|
|
self.cursor.col = limit(self.cursor.col, Column(0), num_cols);
|
2016-06-29 17:21:02 +00:00
|
|
|
|
|
|
|
// Recreate tabs list
|
2016-07-04 00:00:00 +00:00
|
|
|
self.tabs = (Column(0)..self.grid.num_cols()).map(|i| (*i as usize) % TAB_SPACES == 0)
|
|
|
|
.collect::<Vec<bool>>();
|
2016-06-29 17:21:02 +00:00
|
|
|
|
2016-07-30 03:16:13 +00:00
|
|
|
self.tabs[0] = false;
|
|
|
|
|
2016-06-29 17:21:02 +00:00
|
|
|
// Make sure bottom of terminal is clear
|
2016-10-15 22:56:27 +00:00
|
|
|
let template = self.empty_cell.clone();
|
2016-07-30 03:16:13 +00:00
|
|
|
self.grid.clear_region((self.cursor.line).., |c| c.reset(&template));
|
|
|
|
self.alt_grid.clear_region((self.cursor.line).., |c| c.reset(&template));
|
2016-06-29 17:21:02 +00:00
|
|
|
|
|
|
|
// Reset scrolling region to new size
|
2016-07-04 00:00:00 +00:00
|
|
|
self.scroll_region = Line(0)..self.grid.num_lines();
|
2016-06-28 16:18:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn size_info(&self) -> &SizeInfo {
|
|
|
|
&self.size_info
|
|
|
|
}
|
|
|
|
|
2016-06-08 04:17:48 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn mode(&self) -> &TermMode {
|
|
|
|
&self.mode
|
|
|
|
}
|
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
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 {
|
2016-10-15 22:56:27 +00:00
|
|
|
let template = self.empty_cell.clone();
|
2016-07-30 03:16:13 +00:00
|
|
|
self.grid.clear(|c| c.reset(&template));
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
|
|
|
}
|
2016-08-22 15:37:50 +00:00
|
|
|
|
|
|
|
/// Scroll screen down
|
|
|
|
///
|
|
|
|
/// Text moves down; clear at bottom
|
|
|
|
#[inline]
|
|
|
|
fn scroll_down_relative(&mut self, origin: Line, lines: Line) {
|
|
|
|
debug_println!("scroll_down: {}", lines);
|
|
|
|
|
|
|
|
// Copy of cell template; can't have it borrowed when calling clear/scroll
|
2016-10-15 22:56:27 +00:00
|
|
|
let template = self.empty_cell.clone();
|
2016-08-22 15:37:50 +00:00
|
|
|
|
|
|
|
// Clear `lines` lines at bottom of area
|
|
|
|
{
|
|
|
|
let end = self.scroll_region.end;
|
|
|
|
let start = end - lines;
|
|
|
|
self.grid.clear_region(start..end, |c| c.reset(&template));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scroll between origin and bottom
|
|
|
|
{
|
|
|
|
let end = self.scroll_region.end;
|
|
|
|
let start = origin + lines;
|
|
|
|
self.grid.scroll_down(start..end, lines);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Scroll screen up
|
|
|
|
///
|
|
|
|
/// Text moves up; clear at top
|
|
|
|
#[inline]
|
|
|
|
fn scroll_up_relative(&mut self, origin: Line, lines: Line) {
|
|
|
|
debug_println!("scroll_up: {}", lines);
|
|
|
|
|
|
|
|
// Copy of cell template; can't have it borrowed when calling clear/scroll
|
2016-10-15 22:56:27 +00:00
|
|
|
let template = self.empty_cell.clone();
|
2016-08-22 15:37:50 +00:00
|
|
|
|
|
|
|
// Clear `lines` lines starting from origin to origin + lines
|
|
|
|
{
|
|
|
|
let start = origin;
|
|
|
|
let end = start + lines;
|
|
|
|
self.grid.clear_region(start..end, |c| c.reset(&template));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scroll from origin to bottom less number of lines
|
|
|
|
{
|
|
|
|
let start = origin;
|
|
|
|
let end = self.scroll_region.end - lines;
|
|
|
|
self.grid.scroll_up(start..end, lines);
|
|
|
|
}
|
|
|
|
}
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ansi::TermInfo for Term {
|
|
|
|
#[inline]
|
2016-07-04 04:12:43 +00:00
|
|
|
fn lines(&self) -> Line {
|
|
|
|
self.grid.num_lines()
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 04:12:43 +00:00
|
|
|
fn cols(&self) -> Column {
|
|
|
|
self.grid.num_cols()
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ansi::Handler for Term {
|
|
|
|
/// A character to be displayed
|
|
|
|
#[inline]
|
|
|
|
fn input(&mut self, c: char) {
|
2016-07-30 03:16:13 +00:00
|
|
|
if self.cursor.col == self.grid.num_cols() {
|
|
|
|
debug_println!("wrapping");
|
2016-08-20 01:06:33 +00:00
|
|
|
if (self.cursor.line + 1) >= self.scroll_region.end {
|
2016-08-20 00:55:44 +00:00
|
|
|
self.linefeed();
|
|
|
|
} else {
|
|
|
|
self.cursor.line += 1;
|
|
|
|
}
|
2016-07-30 03:16:13 +00:00
|
|
|
self.cursor.col = Column(0);
|
|
|
|
}
|
|
|
|
|
2016-09-19 01:17:33 +00:00
|
|
|
unsafe {
|
|
|
|
if ::std::intrinsics::unlikely(self.cursor.line == self.grid.num_lines()) {
|
|
|
|
panic!("cursor fell off grid");
|
|
|
|
}
|
2016-07-30 03:16:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let cell = &mut self.grid[&self.cursor];
|
2016-10-23 22:37:06 +00:00
|
|
|
*cell = self.template_cell.clone();
|
2016-07-30 03:16:13 +00:00
|
|
|
cell.c = c;
|
2016-07-04 00:00:00 +00:00
|
|
|
self.cursor.col += 1;
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:00:00 +00:00
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn goto(&mut self, line: Line, col: Column) {
|
2016-07-04 15:42:18 +00:00
|
|
|
debug_println!("goto: line={}, col={}", line, col);
|
2016-07-04 04:58:35 +00:00
|
|
|
self.cursor.line = line;
|
|
|
|
self.cursor.col = col;
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
2016-07-04 00:00:00 +00:00
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn goto_line(&mut self, line: Line) {
|
2016-07-04 15:42:18 +00:00
|
|
|
debug_println!("goto_line: {}", line);
|
2016-07-04 04:58:35 +00:00
|
|
|
self.cursor.line = line;
|
2016-06-06 22:13:45 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn goto_col(&mut self, col: Column) {
|
2016-07-04 15:42:18 +00:00
|
|
|
debug_println!("goto_col: {}", col);
|
2016-07-04 04:58:35 +00:00
|
|
|
self.cursor.col = col;
|
2016-06-06 22:13:45 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-10-10 15:29:51 +00:00
|
|
|
fn insert_blank(&mut self, count: Column) {
|
|
|
|
// Ensure inserting within terminal bounds
|
|
|
|
let count = ::std::cmp::min(count, self.size_info.cols() - self.cursor.col);
|
|
|
|
|
|
|
|
let source = self.cursor.col;
|
|
|
|
let destination = self.cursor.col + count;
|
|
|
|
let num_cells = (self.size_info.cols() - destination).0;
|
|
|
|
|
|
|
|
let line = self.cursor.line; // borrowck
|
|
|
|
let line = &mut self.grid[line];
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
let src = line[source..].as_ptr();
|
|
|
|
let dst = line[destination..].as_mut_ptr();
|
|
|
|
|
|
|
|
ptr::copy(src, dst, num_cells);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cells were just moved out towards the end of the line; fill in
|
|
|
|
// between source and dest with blanks.
|
2016-10-15 22:56:27 +00:00
|
|
|
let template = self.empty_cell.clone();
|
2016-10-10 15:29:51 +00:00
|
|
|
for c in &mut line[source..destination] {
|
|
|
|
c.reset(&template);
|
|
|
|
}
|
2016-07-04 20:37:08 +00:00
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn move_up(&mut self, lines: Line) {
|
2016-07-04 15:42:18 +00:00
|
|
|
debug_println!("move_up: {}", lines);
|
2016-07-04 04:58:35 +00:00
|
|
|
self.cursor.line -= lines;
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn move_down(&mut self, lines: Line) {
|
2016-07-04 15:42:18 +00:00
|
|
|
debug_println!("move_down: {}", lines);
|
2016-07-04 04:58:35 +00:00
|
|
|
self.cursor.line += lines;
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn move_forward(&mut self, cols: Column) {
|
2016-07-04 15:42:18 +00:00
|
|
|
debug_println!("move_forward: {}", cols);
|
2016-07-04 04:58:35 +00:00
|
|
|
self.cursor.col += cols;
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn move_backward(&mut self, cols: Column) {
|
2016-07-04 15:42:18 +00:00
|
|
|
debug_println!("move_backward: {}", cols);
|
2016-07-04 04:58:35 +00:00
|
|
|
self.cursor.col -= cols;
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-12-04 23:48:30 +00:00
|
|
|
fn identify_terminal<W: io::Write>(&mut self, writer: &mut W) {
|
|
|
|
let _ = writer.write_all("\x1b[?6c".as_bytes());
|
2016-07-04 20:37:08 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 20:37:08 +00:00
|
|
|
fn move_down_and_cr(&mut self, lines: Line) {
|
2016-08-22 15:34:09 +00:00
|
|
|
err_println!("[unimplemented] move_down_and_cr: {}", lines);
|
2016-07-04 20:37:08 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 20:37:08 +00:00
|
|
|
fn move_up_and_cr(&mut self, lines: Line) {
|
2016-08-22 15:34:09 +00:00
|
|
|
err_println!("[unimplemented] move_up_and_cr: {}", lines);
|
2016-07-04 20:37:08 +00:00
|
|
|
}
|
2016-07-04 00:00:00 +00:00
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-05-31 03:44:37 +00:00
|
|
|
fn put_tab(&mut self, mut count: i64) {
|
2016-07-04 15:42:18 +00:00
|
|
|
debug_println!("put_tab: {}", count);
|
2016-05-31 03:44:37 +00:00
|
|
|
|
2016-07-04 00:00:00 +00:00
|
|
|
let mut col = self.cursor.col;
|
|
|
|
while col < self.grid.num_cols() && count != 0 {
|
2016-05-31 03:44:37 +00:00
|
|
|
count -= 1;
|
|
|
|
loop {
|
2016-07-04 00:00:00 +00:00
|
|
|
if col == self.grid.num_cols() || self.tabs[*col as usize] {
|
2016-05-31 03:44:37 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-07-04 00:00:00 +00:00
|
|
|
col += 1;
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-04 00:00:00 +00:00
|
|
|
self.cursor.col = col;
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Backspace `count` characters
|
|
|
|
#[inline]
|
2016-06-09 15:37:59 +00:00
|
|
|
fn backspace(&mut self) {
|
2016-07-04 15:42:18 +00:00
|
|
|
debug_println!("backspace");
|
2016-12-10 18:14:45 +00:00
|
|
|
if self.cursor.col > Column(0) {
|
|
|
|
self.cursor.col -= 1;
|
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Carriage return
|
|
|
|
#[inline]
|
|
|
|
fn carriage_return(&mut self) {
|
2016-07-04 15:42:18 +00:00
|
|
|
debug_println!("carriage_return");
|
2016-07-04 00:00:00 +00:00
|
|
|
self.cursor.col = Column(0);
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Linefeed
|
|
|
|
#[inline]
|
|
|
|
fn linefeed(&mut self) {
|
2016-07-04 15:42:18 +00:00
|
|
|
debug_println!("linefeed");
|
2016-09-26 02:26:48 +00:00
|
|
|
if self.cursor.line + 1 == self.scroll_region.end {
|
2016-08-20 03:35:13 +00:00
|
|
|
self.scroll_up(Line(1));
|
2016-05-31 03:44:37 +00:00
|
|
|
} else {
|
2016-07-04 00:00:00 +00:00
|
|
|
self.cursor.line += 1;
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set current position as a tabstop
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn bell(&mut self) {
|
|
|
|
debug_println!("bell");
|
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn substitute(&mut self) {
|
2016-08-22 15:34:09 +00:00
|
|
|
err_println!("[unimplemented] substitute");
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn newline(&mut self) {
|
2016-08-22 15:34:09 +00:00
|
|
|
err_println!("[unimplemented] newline");
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn set_horizontal_tabstop(&mut self) {
|
2016-08-22 15:34:09 +00:00
|
|
|
err_println!("[unimplemented] set_horizontal_tabstop");
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-08-22 15:37:50 +00:00
|
|
|
fn scroll_up(&mut self, lines: Line) {
|
|
|
|
let origin = self.scroll_region.start;
|
|
|
|
self.scroll_up_relative(origin, lines);
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-08-22 15:37:50 +00:00
|
|
|
fn scroll_down(&mut self, lines: Line) {
|
|
|
|
let origin = self.scroll_region.start;
|
|
|
|
self.scroll_down_relative(origin, lines);
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn insert_blank_lines(&mut self, lines: Line) {
|
2016-07-04 15:42:18 +00:00
|
|
|
debug_println!("insert_blank_lines: {}", lines);
|
2016-08-22 15:37:50 +00:00
|
|
|
if self.scroll_region.contains(self.cursor.line) {
|
|
|
|
let origin = self.cursor.line;
|
|
|
|
self.scroll_down_relative(origin, lines);
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn delete_lines(&mut self, lines: Line) {
|
2016-07-04 15:42:18 +00:00
|
|
|
debug_println!("delete_lines: {}", lines);
|
2016-08-22 15:37:50 +00:00
|
|
|
if self.scroll_region.contains(self.cursor.line) {
|
|
|
|
let origin = self.cursor.line;
|
|
|
|
self.scroll_up_relative(origin, lines);
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn erase_chars(&mut self, count: Column) {
|
2016-07-04 15:42:18 +00:00
|
|
|
debug_println!("erase_chars: {}", count);
|
2016-07-04 04:58:35 +00:00
|
|
|
let start = self.cursor.col;
|
|
|
|
let end = start + count;
|
2016-07-02 04:13:09 +00:00
|
|
|
|
2016-07-04 00:00:00 +00:00
|
|
|
let row = &mut self.grid[self.cursor.line];
|
2016-10-15 22:56:27 +00:00
|
|
|
let template = self.empty_cell.clone();
|
2016-07-04 04:58:35 +00:00
|
|
|
for c in &mut row[start..end] {
|
2016-07-30 03:16:13 +00:00
|
|
|
c.reset(&template);
|
2016-07-02 04:13:09 +00:00
|
|
|
}
|
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn delete_chars(&mut self, count: Column) {
|
2016-09-15 15:49:55 +00:00
|
|
|
// Ensure deleting within terminal bounds
|
|
|
|
let count = ::std::cmp::min(count, self.size_info.cols());
|
|
|
|
|
|
|
|
let start = self.cursor.col;
|
|
|
|
let end = self.cursor.col + count;
|
|
|
|
let n = (self.size_info.cols() - end).0;
|
|
|
|
|
|
|
|
let line = self.cursor.line; // borrowck
|
|
|
|
let line = &mut self.grid[line];
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
let src = line[end..].as_ptr();
|
|
|
|
let dst = line[start..].as_mut_ptr();
|
|
|
|
|
|
|
|
ptr::copy(src, dst, n);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear last `count` cells in line. If deleting 1 char, need to delete 1 cell.
|
2016-10-15 22:56:27 +00:00
|
|
|
let template = self.empty_cell.clone();
|
2016-09-15 15:49:55 +00:00
|
|
|
let end = self.size_info.cols() - count;
|
|
|
|
for c in &mut line[end..] {
|
|
|
|
c.reset(&template);
|
|
|
|
}
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn move_backward_tabs(&mut self, count: i64) {
|
2016-08-22 15:34:09 +00:00
|
|
|
err_println!("[unimplemented] move_backward_tabs: {}", count);
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn move_forward_tabs(&mut self, count: i64) {
|
2016-08-22 15:34:09 +00:00
|
|
|
err_println!("[unimplemented] move_forward_tabs: {}", count);
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn save_cursor_position(&mut self) {
|
2016-08-22 15:34:09 +00:00
|
|
|
err_println!("[unimplemented] save_cursor_position");
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn restore_cursor_position(&mut self) {
|
2016-08-22 15:34:09 +00:00
|
|
|
err_println!("[unimplemented] restore_cursor_position");
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-05-31 03:44:37 +00:00
|
|
|
fn clear_line(&mut self, mode: ansi::LineClearMode) {
|
2016-07-04 15:42:18 +00:00
|
|
|
debug_println!("clear_line: {:?}", mode);
|
2016-10-15 22:56:27 +00:00
|
|
|
let template = self.empty_cell.clone();
|
2016-05-31 03:44:37 +00:00
|
|
|
match mode {
|
|
|
|
ansi::LineClearMode::Right => {
|
2016-07-04 00:00:00 +00:00
|
|
|
let row = &mut self.grid[self.cursor.line];
|
|
|
|
for cell in &mut row[self.cursor.col..] {
|
2016-07-30 03:16:13 +00:00
|
|
|
cell.reset(&template);
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
},
|
2016-08-22 15:11:34 +00:00
|
|
|
ansi::LineClearMode::Left => {
|
|
|
|
let row = &mut self.grid[self.cursor.line];
|
|
|
|
for cell in &mut row[..(self.cursor.col + 1)] {
|
|
|
|
cell.reset(&template);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
ansi::LineClearMode::All => {
|
|
|
|
let row = &mut self.grid[self.cursor.line];
|
|
|
|
for cell in &mut row[..] {
|
|
|
|
cell.reset(&template);
|
|
|
|
}
|
|
|
|
},
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-05-31 03:44:37 +00:00
|
|
|
fn clear_screen(&mut self, mode: ansi::ClearMode) {
|
2016-07-04 15:42:18 +00:00
|
|
|
debug_println!("clear_screen: {:?}", mode);
|
2016-10-15 22:56:27 +00:00
|
|
|
let template = self.empty_cell.clone();
|
2016-05-31 03:44:37 +00:00
|
|
|
match mode {
|
|
|
|
ansi::ClearMode::Below => {
|
2016-08-22 15:37:50 +00:00
|
|
|
for row in &mut self.grid[self.cursor.line..] {
|
2016-07-02 15:25:21 +00:00
|
|
|
for cell in row {
|
2016-07-30 03:16:13 +00:00
|
|
|
cell.reset(&template);
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
ansi::ClearMode::All => {
|
2016-07-30 03:16:13 +00:00
|
|
|
self.grid.clear(|c| c.reset(&template));
|
2016-05-31 03:44:37 +00:00
|
|
|
},
|
|
|
|
_ => {
|
|
|
|
panic!("ansi::ClearMode::Above not implemented");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn clear_tabs(&mut self, mode: ansi::TabulationClearMode) {
|
2016-08-22 15:34:09 +00:00
|
|
|
err_println!("[unimplemented] clear_tabs: {:?}", mode);
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn reset_state(&mut self) {
|
2016-08-22 15:34:09 +00:00
|
|
|
err_println!("[unimplemented] reset_state");
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
2016-05-31 03:44:37 +00:00
|
|
|
fn reverse_index(&mut self) {
|
2016-07-04 15:42:18 +00:00
|
|
|
debug_println!("reverse_index");
|
2016-05-31 03:44:37 +00:00
|
|
|
// if cursor is at the top
|
2016-08-22 15:37:50 +00:00
|
|
|
if self.cursor.line == self.scroll_region.start {
|
2016-08-20 03:35:13 +00:00
|
|
|
self.scroll_down(Line(1));
|
2016-05-31 03:44:37 +00:00
|
|
|
} else {
|
2016-08-20 03:35:13 +00:00
|
|
|
self.cursor.line -= 1;
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// set a terminal attribute
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-05-31 03:44:37 +00:00
|
|
|
fn terminal_attribute(&mut self, attr: Attr) {
|
2016-07-16 19:39:05 +00:00
|
|
|
debug_println!("Set Attribute: {:?}", attr);
|
2016-05-31 03:44:37 +00:00
|
|
|
match attr {
|
2016-12-04 19:14:27 +00:00
|
|
|
Attr::Foreground(color) => self.template_cell.fg = color,
|
|
|
|
Attr::Background(color) => self.template_cell.bg = color,
|
2016-05-31 03:44:37 +00:00
|
|
|
Attr::Reset => {
|
2016-12-04 19:14:27 +00:00
|
|
|
self.template_cell.fg = Color::Named(NamedColor::Foreground);
|
|
|
|
self.template_cell.bg = Color::Named(NamedColor::Background);
|
2016-10-15 22:56:27 +00:00
|
|
|
self.template_cell.flags = cell::Flags::empty();
|
2016-06-06 23:54:15 +00:00
|
|
|
},
|
2016-10-15 22:56:27 +00:00
|
|
|
Attr::Reverse => self.template_cell.flags.insert(cell::INVERSE),
|
|
|
|
Attr::CancelReverse => self.template_cell.flags.remove(cell::INVERSE),
|
|
|
|
Attr::Bold => self.template_cell.flags.insert(cell::BOLD),
|
|
|
|
Attr::CancelBoldDim => self.template_cell.flags.remove(cell::BOLD),
|
|
|
|
Attr::Italic => self.template_cell.flags.insert(cell::ITALIC),
|
|
|
|
Attr::CancelItalic => self.template_cell.flags.remove(cell::ITALIC),
|
|
|
|
Attr::Underscore => self.template_cell.flags.insert(cell::UNDERLINE),
|
|
|
|
Attr::CancelUnderline => self.template_cell.flags.remove(cell::UNDERLINE),
|
2016-05-31 03:44:37 +00:00
|
|
|
_ => {
|
2016-07-04 15:42:18 +00:00
|
|
|
debug_println!("Term got unhandled attr: {:?}", attr);
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-05-31 03:44:37 +00:00
|
|
|
fn set_mode(&mut self, mode: ansi::Mode) {
|
2016-07-04 15:42:18 +00:00
|
|
|
debug_println!("set_mode: {:?}", mode);
|
2016-05-31 03:44:37 +00:00
|
|
|
match mode {
|
2016-06-08 04:17:48 +00:00
|
|
|
ansi::Mode::SwapScreenAndSetRestoreCursor => self.swap_alt(),
|
2016-06-23 16:42:00 +00:00
|
|
|
ansi::Mode::ShowCursor => self.mode.insert(mode::SHOW_CURSOR),
|
2016-06-23 16:48:31 +00:00
|
|
|
ansi::Mode::CursorKeys => self.mode.insert(mode::APP_CURSOR),
|
2016-11-24 04:25:37 +00:00
|
|
|
ansi::Mode::ReportMouseClicks => self.mode.insert(mode::MOUSE_REPORT_CLICK),
|
2016-11-28 22:39:37 +00:00
|
|
|
ansi::Mode::BracketedPaste => self.mode.insert(mode::BRACKETED_PASTE),
|
2016-06-08 04:17:48 +00:00
|
|
|
_ => {
|
2016-07-04 15:42:18 +00:00
|
|
|
debug_println!(".. ignoring set_mode");
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-06-08 04:17:48 +00:00
|
|
|
fn unset_mode(&mut self,mode: ansi::Mode) {
|
2016-07-04 15:42:18 +00:00
|
|
|
debug_println!("unset_mode: {:?}", mode);
|
2016-05-31 03:44:37 +00:00
|
|
|
match mode {
|
2016-06-08 04:17:48 +00:00
|
|
|
ansi::Mode::SwapScreenAndSetRestoreCursor => self.swap_alt(),
|
2016-06-23 16:42:00 +00:00
|
|
|
ansi::Mode::ShowCursor => self.mode.remove(mode::SHOW_CURSOR),
|
2016-06-23 16:48:31 +00:00
|
|
|
ansi::Mode::CursorKeys => self.mode.remove(mode::APP_CURSOR),
|
2016-11-24 04:25:37 +00:00
|
|
|
ansi::Mode::ReportMouseClicks => self.mode.remove(mode::MOUSE_REPORT_CLICK),
|
2016-11-28 22:39:37 +00:00
|
|
|
ansi::Mode::BracketedPaste => self.mode.remove(mode::BRACKETED_PASTE),
|
2016-06-08 04:17:48 +00:00
|
|
|
_ => {
|
2016-07-04 15:42:18 +00:00
|
|
|
debug_println!(".. ignoring unset_mode");
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-06-08 17:39:49 +00:00
|
|
|
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 04:58:35 +00:00
|
|
|
fn set_scrolling_region(&mut self, region: Range<Line>) {
|
2016-07-04 15:42:18 +00:00
|
|
|
debug_println!("set scroll region: {:?}", region);
|
2016-07-04 04:58:35 +00:00
|
|
|
self.scroll_region = region;
|
2016-08-22 15:37:50 +00:00
|
|
|
self.goto(Line(0), Column(0));
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
2016-07-04 16:19:48 +00:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn set_keypad_application_mode(&mut self) {
|
|
|
|
debug_println!("set mode::APP_KEYPAD");
|
|
|
|
self.mode.insert(mode::APP_KEYPAD);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn unset_keypad_application_mode(&mut self) {
|
|
|
|
debug_println!("unset mode::APP_KEYPAD");
|
|
|
|
self.mode.remove(mode::APP_KEYPAD);
|
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
2016-11-20 00:16:20 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
extern crate serde_json;
|
2016-11-25 20:03:11 +00:00
|
|
|
extern crate test;
|
|
|
|
|
|
|
|
use super::limit;
|
2016-11-20 00:16:20 +00:00
|
|
|
|
2016-12-04 19:14:27 +00:00
|
|
|
use ansi::{Color, NamedColor};
|
2016-11-20 00:16:20 +00:00
|
|
|
use grid::Grid;
|
|
|
|
use index::{Line, Column};
|
2016-12-04 19:14:27 +00:00
|
|
|
use term::{Cell};
|
2016-11-20 00:16:20 +00:00
|
|
|
|
|
|
|
/// Check that the grid can be serialized back and forth losslessly
|
|
|
|
///
|
|
|
|
/// This test is in the term module as opposed to the grid since we want to
|
|
|
|
/// test this property with a T=Cell.
|
|
|
|
#[test]
|
|
|
|
fn grid_serde() {
|
|
|
|
let template = Cell::new(
|
|
|
|
' ',
|
2016-12-04 19:14:27 +00:00
|
|
|
Color::Named(NamedColor::Foreground),
|
|
|
|
Color::Named(NamedColor::Background)
|
2016-11-20 00:16:20 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
let grid = Grid::new(Line(24), Column(80), &template);
|
|
|
|
let serialized = serde_json::to_string(&grid).expect("ser");
|
|
|
|
let deserialized = serde_json::from_str::<Grid<Cell>>(&serialized)
|
|
|
|
.expect("de");
|
|
|
|
|
|
|
|
assert_eq!(deserialized, grid);
|
|
|
|
}
|
2016-11-25 20:03:11 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn limit_works() {
|
|
|
|
assert_eq!(limit(5, 1, 10), 5);
|
|
|
|
assert_eq!(limit(5, 6, 10), 6);
|
|
|
|
assert_eq!(limit(5, 1, 4), 4);
|
|
|
|
}
|
2016-11-20 00:16:20 +00:00
|
|
|
}
|
2016-11-28 22:13:11 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod bench {
|
|
|
|
extern crate test;
|
|
|
|
extern crate serde_json as json;
|
|
|
|
|
|
|
|
use std::io::Read;
|
|
|
|
use std::fs::File;
|
|
|
|
use std::mem;
|
|
|
|
use std::path::Path;
|
|
|
|
|
|
|
|
use grid::Grid;
|
|
|
|
|
|
|
|
use super::{SizeInfo, Term};
|
|
|
|
use super::cell::Cell;
|
|
|
|
|
|
|
|
fn read_string<P>(path: P) -> String
|
|
|
|
where P: AsRef<Path>
|
|
|
|
{
|
|
|
|
let mut res = String::new();
|
|
|
|
File::open(path.as_ref()).unwrap()
|
|
|
|
.read_to_string(&mut res).unwrap();
|
|
|
|
|
|
|
|
res
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Benchmark for the renderable cells iterator
|
|
|
|
///
|
|
|
|
/// The renderable cells iterator yields cells that require work to be displayed (that is, not a
|
|
|
|
/// an empty background cell). This benchmark measures how long it takes to process the whole
|
|
|
|
/// iterator.
|
|
|
|
///
|
|
|
|
/// When this benchmark was first added, it averaged ~78usec on my macbook pro. The total
|
|
|
|
/// render time for this grid is anywhere between ~1500 and ~2000usec (measured imprecisely with
|
|
|
|
/// the visual meter).
|
|
|
|
#[bench]
|
|
|
|
fn render_iter(b: &mut test::Bencher) {
|
|
|
|
// Need some realistic grid state; using one of the ref files.
|
|
|
|
let serialized_grid = read_string(
|
|
|
|
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/ref/vim_large_window_scroll/grid.json")
|
|
|
|
);
|
|
|
|
let serialized_size = read_string(
|
|
|
|
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/ref/vim_large_window_scroll/size.json")
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut grid: Grid<Cell> = json::from_str(&serialized_grid).unwrap();
|
|
|
|
let size: SizeInfo = json::from_str(&serialized_size).unwrap();
|
|
|
|
|
|
|
|
let mut terminal = Term::new(size);
|
|
|
|
mem::swap(&mut terminal.grid, &mut grid);
|
|
|
|
|
|
|
|
b.iter(|| {
|
|
|
|
let iter = terminal.renderable_cells();
|
|
|
|
for cell in iter {
|
|
|
|
test::black_box(cell);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|