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
|
2017-02-11 20:49:40 +00:00
|
|
|
use std::ops::{Range, Index, IndexMut};
|
2016-09-15 15:49:55 +00:00
|
|
|
use std::ptr;
|
2017-04-20 03:08:44 +00:00
|
|
|
use std::cmp::{min, max};
|
2016-12-04 23:48:30 +00:00
|
|
|
use std::io;
|
2017-02-03 23:34:52 +00:00
|
|
|
use std::time::{Duration, Instant};
|
2016-05-31 03:44:37 +00:00
|
|
|
|
2017-05-28 16:38:10 +00:00
|
|
|
use arraydeque::ArrayDeque;
|
2017-03-02 06:24:37 +00:00
|
|
|
use unicode_width::UnicodeWidthChar;
|
|
|
|
|
2018-01-05 03:22:58 +00:00
|
|
|
use font::{self, Size};
|
2017-05-28 16:38:10 +00:00
|
|
|
use ansi::{self, Color, NamedColor, Attr, Handler, CharsetIndex, StandardCharset, CursorStyle};
|
2018-03-11 12:01:06 +00:00
|
|
|
use grid::{BidirectionalIterator, Grid, Indexed, IndexRegion, DisplayIter, Scroll};
|
2018-03-07 04:57:40 +00:00
|
|
|
use index::{self, Point, Column, Line, IndexRange, Contains, RangeInclusive, Linear};
|
|
|
|
use selection::{self, Selection, Locations};
|
2017-02-03 23:34:52 +00:00
|
|
|
use config::{Config, VisualBellAnimation};
|
2017-12-24 20:15:42 +00:00
|
|
|
use {MouseCursor, Rgb};
|
2018-01-02 16:32:50 +00:00
|
|
|
use copypasta::{Clipboard, Load, Store};
|
2016-05-31 03:44:37 +00:00
|
|
|
|
2016-11-28 22:30:08 +00:00
|
|
|
pub mod cell;
|
2017-02-11 20:49:40 +00:00
|
|
|
pub mod color;
|
2016-11-28 22:30:08 +00:00
|
|
|
pub use self::cell::Cell;
|
2016-12-27 03:52:37 +00:00
|
|
|
use self::cell::LineLength;
|
2016-11-28 22:30:08 +00:00
|
|
|
|
2018-01-06 01:42:55 +00:00
|
|
|
impl selection::SemanticSearch for Term {
|
2018-03-07 04:57:40 +00:00
|
|
|
fn semantic_search_left(&self, mut point: Point<usize>) -> Point<usize> {
|
2017-06-16 04:43:28 +00:00
|
|
|
let mut iter = self.grid.iter_from(point);
|
|
|
|
let last_col = self.grid.num_cols() - Column(1);
|
|
|
|
|
|
|
|
while let Some(cell) = iter.prev() {
|
|
|
|
if self.semantic_escape_chars.contains(cell.c) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-10-12 01:52:23 +00:00
|
|
|
if iter.cur.col == last_col && !cell.flags.contains(cell::Flags::WRAPLINE) {
|
2017-06-16 04:43:28 +00:00
|
|
|
break; // cut off if on new line or hit escape char
|
|
|
|
}
|
|
|
|
|
|
|
|
point = iter.cur;
|
|
|
|
}
|
|
|
|
|
|
|
|
point
|
|
|
|
}
|
|
|
|
|
2018-03-07 04:57:40 +00:00
|
|
|
fn semantic_search_right(&self, mut point: Point<usize>) -> Point<usize> {
|
2017-06-16 04:43:28 +00:00
|
|
|
let mut iter = self.grid.iter_from(point);
|
|
|
|
let last_col = self.grid.num_cols() - Column(1);
|
|
|
|
|
|
|
|
while let Some(cell) = iter.next() {
|
|
|
|
if self.semantic_escape_chars.contains(cell.c) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
point = iter.cur;
|
|
|
|
|
2017-10-12 01:52:23 +00:00
|
|
|
if iter.cur.col == last_col && !cell.flags.contains(cell::Flags::WRAPLINE) {
|
2017-06-16 04:43:28 +00:00
|
|
|
break; // cut off if on new line or hit escape char
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
point
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-06 01:42:55 +00:00
|
|
|
impl selection::Dimensions for Term {
|
2017-06-16 04:43:28 +00:00
|
|
|
fn dimensions(&self) -> Point {
|
|
|
|
Point {
|
|
|
|
col: self.grid.num_cols(),
|
|
|
|
line: self.grid.num_lines()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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> {
|
2018-02-16 02:35:49 +00:00
|
|
|
inner: DisplayIter<'a, Cell>,
|
2017-05-28 16:38:10 +00:00
|
|
|
grid: &'a Grid<Cell>,
|
2016-12-29 16:09:29 +00:00
|
|
|
cursor: &'a Point,
|
2018-02-16 02:35:49 +00:00
|
|
|
cursor_offset: usize,
|
2016-07-04 21:46:25 +00:00
|
|
|
mode: TermMode,
|
2017-02-11 20:49:40 +00:00
|
|
|
config: &'a Config,
|
|
|
|
colors: &'a color::List,
|
2016-12-22 18:43:06 +00:00
|
|
|
selection: Option<RangeInclusive<index::Linear>>,
|
2018-02-28 20:33:29 +00:00
|
|
|
cursor_cells: ArrayDeque<[Indexed<Cell>; 3]>,
|
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>(
|
2017-05-28 16:38:10 +00:00
|
|
|
grid: &'b Grid<Cell>,
|
2016-12-29 16:09:29 +00:00
|
|
|
cursor: &'b Point,
|
2017-02-11 20:49:40 +00:00
|
|
|
colors: &'b color::List,
|
2016-12-22 18:43:06 +00:00
|
|
|
mode: TermMode,
|
2017-02-11 20:49:40 +00:00
|
|
|
config: &'b Config,
|
2018-03-07 04:57:40 +00:00
|
|
|
selection: Option<Locations>,
|
2017-05-28 16:38:10 +00:00
|
|
|
cursor_style: CursorStyle,
|
2016-11-28 22:13:11 +00:00
|
|
|
) -> RenderableCellsIter<'b> {
|
2018-02-16 02:35:49 +00:00
|
|
|
let cursor_offset = grid.line_to_offset(cursor.line);
|
|
|
|
let inner = grid.display_iter();
|
2017-05-28 16:38:10 +00:00
|
|
|
|
2018-03-10 13:53:54 +00:00
|
|
|
let mut selection_range = None;
|
|
|
|
selection.map(|loc| {
|
|
|
|
// Get on-screen lines of the selection's locations
|
|
|
|
let start_line = grid.buffer_line_to_visible(loc.start.line);
|
|
|
|
let end_line = grid.buffer_line_to_visible(loc.end.line);
|
|
|
|
|
|
|
|
// Get start/end locations based on what part of selection is on screen
|
|
|
|
let locations = match (start_line, end_line) {
|
|
|
|
(Some(start_line), Some(end_line)) => {
|
|
|
|
Some((start_line, loc.start.col, end_line, loc.end.col))
|
|
|
|
},
|
|
|
|
(Some(start_line), None) => {
|
2018-03-13 21:07:23 +00:00
|
|
|
Some((start_line, loc.start.col, Line(0), Column(0)))
|
2018-03-10 13:53:54 +00:00
|
|
|
},
|
|
|
|
(None, Some(end_line)) => {
|
|
|
|
Some((grid.num_lines(), Column(0), end_line, loc.end.col))
|
|
|
|
},
|
|
|
|
(None, None) => None,
|
2018-03-07 04:57:40 +00:00
|
|
|
};
|
|
|
|
|
2018-03-10 13:53:54 +00:00
|
|
|
if let Some((start_line, start_col, end_line, end_col)) = locations {
|
|
|
|
// start and end *lines* are swapped as we switch from buffer to
|
|
|
|
// Line coordinates.
|
|
|
|
let mut end = Point {
|
|
|
|
line: start_line,
|
|
|
|
col: start_col,
|
|
|
|
};
|
|
|
|
let mut start = Point {
|
|
|
|
line: end_line,
|
|
|
|
col: end_col,
|
|
|
|
};
|
|
|
|
|
|
|
|
if start > end {
|
|
|
|
::std::mem::swap(&mut start, &mut end);
|
|
|
|
}
|
|
|
|
|
|
|
|
let cols = grid.num_cols();
|
|
|
|
let start = Linear(start.line.0 * cols.0 + start.col.0);
|
|
|
|
let end = Linear(end.line.0 * cols.0 + end.col.0);
|
|
|
|
|
|
|
|
// Update the selection
|
|
|
|
selection_range = Some(RangeInclusive::new(start, end));
|
2018-03-07 04:57:40 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2016-11-28 22:13:11 +00:00
|
|
|
RenderableCellsIter {
|
2018-02-16 02:35:49 +00:00
|
|
|
cursor: cursor,
|
|
|
|
cursor_offset: cursor_offset,
|
|
|
|
grid: grid,
|
|
|
|
inner: inner,
|
|
|
|
mode: mode,
|
2018-03-10 13:53:54 +00:00
|
|
|
selection: selection_range,
|
2018-02-16 02:35:49 +00:00
|
|
|
config: config,
|
|
|
|
colors: colors,
|
2017-05-28 16:38:10 +00:00
|
|
|
cursor_cells: ArrayDeque::new(),
|
2018-01-06 01:42:55 +00:00
|
|
|
}.initialize(cursor_style)
|
2016-11-28 22:13:11 +00:00
|
|
|
}
|
|
|
|
|
2018-03-05 17:26:36 +00:00
|
|
|
fn push_cursor_cells(&mut self, original: Cell, cursor: Cell, wide: Cell) {
|
2017-12-24 21:23:53 +00:00
|
|
|
// Prints the char under the cell if cursor is situated on a non-empty cell
|
|
|
|
self.cursor_cells.push_back(Indexed {
|
|
|
|
line: self.cursor.line,
|
|
|
|
column: self.cursor.col,
|
2018-03-05 17:26:36 +00:00
|
|
|
inner: original,
|
2018-02-28 20:33:29 +00:00
|
|
|
}).expect("won't exceed capacity");
|
2017-12-24 21:23:53 +00:00
|
|
|
|
|
|
|
// Prints the cursor
|
|
|
|
self.cursor_cells.push_back(Indexed {
|
|
|
|
line: self.cursor.line,
|
|
|
|
column: self.cursor.col,
|
2018-03-05 17:26:36 +00:00
|
|
|
inner: cursor,
|
2018-02-28 20:33:29 +00:00
|
|
|
}).expect("won't exceed capacity");
|
2017-12-24 21:23:53 +00:00
|
|
|
|
|
|
|
// If cursor is over a wide (2 cell size) character,
|
|
|
|
// print the second cursor cell
|
2018-03-05 17:26:36 +00:00
|
|
|
if self.is_wide_cursor(&cursor) {
|
2017-12-24 21:23:53 +00:00
|
|
|
self.cursor_cells.push_back(Indexed {
|
|
|
|
line: self.cursor.line,
|
|
|
|
column: self.cursor.col + 1,
|
2018-03-05 17:26:36 +00:00
|
|
|
inner: wide,
|
2018-02-28 20:33:29 +00:00
|
|
|
}).expect("won't exceed capacity");
|
2017-12-24 21:23:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-13 17:24:12 +00:00
|
|
|
fn populate_block_cursor(&mut self) {
|
2017-05-28 16:38:10 +00:00
|
|
|
let (text_color, cursor_color) = if self.config.custom_cursor_colors() {
|
|
|
|
(
|
|
|
|
Color::Named(NamedColor::CursorText),
|
|
|
|
Color::Named(NamedColor::Cursor)
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
// Swap fg, bg
|
|
|
|
let cell = &self.grid[self.cursor];
|
|
|
|
(cell.bg, cell.fg)
|
|
|
|
};
|
|
|
|
|
2017-12-24 21:23:53 +00:00
|
|
|
let original_cell = self.grid[self.cursor];
|
2017-05-28 16:38:10 +00:00
|
|
|
|
2017-12-24 21:23:53 +00:00
|
|
|
let mut cursor_cell = self.grid[self.cursor];
|
|
|
|
cursor_cell.fg = text_color;
|
|
|
|
cursor_cell.bg = cursor_color;
|
2017-05-28 16:38:10 +00:00
|
|
|
|
2017-12-24 21:23:53 +00:00
|
|
|
let mut wide_cell = cursor_cell;
|
|
|
|
wide_cell.c = ' ';
|
2017-03-02 06:24:37 +00:00
|
|
|
|
2017-12-24 21:23:53 +00:00
|
|
|
self.push_cursor_cells(original_cell, cursor_cell, wide_cell);
|
2017-05-28 16:38:10 +00:00
|
|
|
}
|
|
|
|
|
2017-12-24 21:23:53 +00:00
|
|
|
fn populate_char_cursor(&mut self, cursor_cell_char: char, wide_cell_char: char) {
|
|
|
|
let original_cell = self.grid[self.cursor];
|
|
|
|
|
|
|
|
let mut cursor_cell = self.grid[self.cursor];
|
|
|
|
let cursor_color = self.text_cursor_color(&cursor_cell);
|
|
|
|
cursor_cell.c = cursor_cell_char;
|
|
|
|
cursor_cell.fg = cursor_color;
|
|
|
|
|
|
|
|
let mut wide_cell = cursor_cell;
|
|
|
|
wide_cell.c = wide_cell_char;
|
|
|
|
|
|
|
|
self.push_cursor_cells(original_cell, cursor_cell, wide_cell);
|
2017-05-28 16:38:10 +00:00
|
|
|
}
|
2017-02-11 20:49:40 +00:00
|
|
|
|
2017-05-28 16:38:10 +00:00
|
|
|
fn populate_underline_cursor(&mut self) {
|
2017-12-24 21:23:53 +00:00
|
|
|
self.populate_char_cursor(font::UNDERLINE_CURSOR_CHAR, font::UNDERLINE_CURSOR_CHAR);
|
2017-12-09 23:33:58 +00:00
|
|
|
}
|
|
|
|
|
2017-12-24 21:23:53 +00:00
|
|
|
fn populate_beam_cursor(&mut self) {
|
|
|
|
self.populate_char_cursor(font::BEAM_CURSOR_CHAR, ' ');
|
|
|
|
}
|
2017-05-28 16:38:10 +00:00
|
|
|
|
2018-01-06 01:42:55 +00:00
|
|
|
fn populate_box_cursor(&mut self) {
|
|
|
|
self.populate_char_cursor(font::BOX_CURSOR_CHAR, ' ');
|
|
|
|
}
|
|
|
|
|
2017-12-24 21:23:53 +00:00
|
|
|
#[inline]
|
|
|
|
fn is_wide_cursor(&self, cell: &Cell) -> bool {
|
|
|
|
cell.flags.contains(cell::Flags::WIDE_CHAR) && (self.cursor.col + 1) < self.grid.num_cols()
|
2017-05-28 16:38:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn text_cursor_color(&self, cell: &Cell) -> Color {
|
|
|
|
if self.config.custom_cursor_colors() {
|
|
|
|
Color::Named(NamedColor::Cursor)
|
|
|
|
} else {
|
|
|
|
// Cursor is same color as text
|
|
|
|
cell.fg
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Populates list of cursor cells with the original cell
|
|
|
|
fn populate_no_cursor(&mut self) {
|
|
|
|
self.cursor_cells.push_back(Indexed {
|
|
|
|
line: self.cursor.line,
|
|
|
|
column: self.cursor.col,
|
|
|
|
inner: self.grid[self.cursor],
|
2018-02-28 20:33:29 +00:00
|
|
|
}).expect("won't exceed capacity");
|
2017-05-28 16:38:10 +00:00
|
|
|
}
|
|
|
|
|
2018-01-06 01:42:55 +00:00
|
|
|
fn initialize(mut self, cursor_style: CursorStyle) -> Self {
|
2017-05-28 16:38:10 +00:00
|
|
|
if self.cursor_is_visible() {
|
2018-01-06 01:42:55 +00:00
|
|
|
match cursor_style {
|
|
|
|
CursorStyle::HollowBlock => {
|
|
|
|
self.populate_box_cursor();
|
|
|
|
},
|
|
|
|
CursorStyle::Block => {
|
|
|
|
self.populate_block_cursor();
|
|
|
|
},
|
|
|
|
CursorStyle::Beam => {
|
|
|
|
self.populate_beam_cursor();
|
|
|
|
},
|
|
|
|
CursorStyle::Underline => {
|
|
|
|
self.populate_underline_cursor();
|
2017-03-02 06:24:37 +00:00
|
|
|
}
|
2017-01-09 03:18:39 +00:00
|
|
|
}
|
2017-05-28 16:38:10 +00:00
|
|
|
} else {
|
|
|
|
self.populate_no_cursor();
|
2017-01-09 03:18:39 +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 {
|
2017-10-12 01:52:23 +00:00
|
|
|
self.mode.contains(mode::TermMode::SHOW_CURSOR) && self.grid.contains(self.cursor)
|
2016-07-04 21:46:25 +00:00
|
|
|
}
|
|
|
|
|
2017-05-28 16:38:10 +00:00
|
|
|
fn compute_fg_rgb(&self, fg: &Color, cell: &Cell) -> Rgb {
|
2017-10-12 01:52:23 +00:00
|
|
|
use self::cell::Flags;
|
2017-05-28 16:38:10 +00:00
|
|
|
match *fg {
|
|
|
|
Color::Spec(rgb) => rgb,
|
|
|
|
Color::Named(ansi) => {
|
2017-10-12 01:52:23 +00:00
|
|
|
match (self.config.draw_bold_text_with_bright_colors(), cell.flags & Flags::DIM_BOLD) {
|
2017-06-23 17:01:53 +00:00
|
|
|
// Draw bold text in bright colors *and* contains bold flag.
|
2017-10-12 01:52:23 +00:00
|
|
|
(true, self::cell::Flags::DIM_BOLD) |
|
|
|
|
(true, self::cell::Flags::BOLD) => self.colors[ansi.to_bright()],
|
2017-06-23 17:01:53 +00:00
|
|
|
// Cell is marked as dim and not bold
|
2017-10-12 01:52:23 +00:00
|
|
|
(_, self::cell::Flags::DIM) => self.colors[ansi.to_dim()],
|
2017-06-23 17:01:53 +00:00
|
|
|
// None of the above, keep original color.
|
|
|
|
_ => self.colors[ansi]
|
2017-05-28 16:38:10 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
Color::Indexed(idx) => {
|
2017-06-23 17:01:53 +00:00
|
|
|
let idx = match (
|
|
|
|
self.config.draw_bold_text_with_bright_colors(),
|
2017-10-12 01:52:23 +00:00
|
|
|
cell.flags & Flags::DIM_BOLD,
|
2017-05-28 16:38:10 +00:00
|
|
|
idx
|
2017-06-23 17:01:53 +00:00
|
|
|
) {
|
2017-10-12 01:52:23 +00:00
|
|
|
(true, self::cell::Flags::BOLD, 0...7) => idx as usize + 8,
|
|
|
|
(false, self::cell::Flags::DIM, 8...15) => idx as usize - 8,
|
|
|
|
(false, self::cell::Flags::DIM, 0...7) => idx as usize + 260,
|
2017-06-23 17:01:53 +00:00
|
|
|
_ => idx as usize,
|
2017-05-28 16:38:10 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
self.colors[idx]
|
2017-03-02 06:24:37 +00:00
|
|
|
}
|
2016-07-04 21:46:25 +00:00
|
|
|
}
|
|
|
|
}
|
2017-05-28 16:38:10 +00:00
|
|
|
|
2017-10-21 22:26:42 +00:00
|
|
|
#[inline]
|
|
|
|
fn compute_bg_alpha(&self, bg: &Color) -> f32 {
|
|
|
|
match *bg {
|
|
|
|
Color::Named(NamedColor::Background) => 0.0,
|
|
|
|
_ => 1.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-28 16:38:10 +00:00
|
|
|
fn compute_bg_rgb(&self, bg: &Color) -> Rgb {
|
|
|
|
match *bg {
|
|
|
|
Color::Spec(rgb) => rgb,
|
|
|
|
Color::Named(ansi) => self.colors[ansi],
|
|
|
|
Color::Indexed(idx) => self.colors[idx],
|
|
|
|
}
|
|
|
|
}
|
2016-07-04 21:46:25 +00:00
|
|
|
}
|
|
|
|
|
2017-02-11 20:49:40 +00:00
|
|
|
pub struct RenderableCell {
|
2018-02-16 02:35:49 +00:00
|
|
|
/// A _Display_ line (not necessarily an _Active_ line)
|
2016-11-28 22:13:11 +00:00
|
|
|
pub line: Line,
|
|
|
|
pub column: Column,
|
2017-02-11 20:49:40 +00:00
|
|
|
pub c: char,
|
|
|
|
pub fg: Rgb,
|
|
|
|
pub bg: Rgb,
|
2017-10-21 22:26:42 +00:00
|
|
|
pub bg_alpha: f32,
|
2017-02-11 20:49:40 +00:00
|
|
|
pub flags: cell::Flags,
|
2016-11-28 22:13:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Iterator for RenderableCellsIter<'a> {
|
2017-02-11 20:49:40 +00:00
|
|
|
type Item = RenderableCell;
|
2016-11-28 22:13:11 +00:00
|
|
|
|
|
|
|
/// Gets the next renderable cell
|
|
|
|
///
|
|
|
|
/// Skips empty (background) cells and applies any flags to the cell state
|
|
|
|
/// (eg. invert fg and bg colors).
|
2016-12-17 06:13:51 +00:00
|
|
|
#[inline]
|
2016-11-28 22:13:11 +00:00
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
2018-02-16 02:35:49 +00:00
|
|
|
loop {
|
|
|
|
// Handle cursor
|
|
|
|
let (cell, selected) = if self.cursor_offset == self.inner.offset() &&
|
|
|
|
self.inner.column() == self.cursor.col
|
|
|
|
{
|
|
|
|
// Cursor cell
|
2018-02-16 03:34:09 +00:00
|
|
|
let mut cell = self.cursor_cells.pop_front().unwrap();
|
|
|
|
cell.line = self.inner.line();
|
2018-02-16 02:35:49 +00:00
|
|
|
|
|
|
|
// Since there may be multiple cursor cells (for a wide
|
|
|
|
// char), only update iteration position after all cursor
|
|
|
|
// cells have been drawn.
|
|
|
|
if self.cursor_cells.is_empty() {
|
|
|
|
self.inner.next();
|
|
|
|
}
|
|
|
|
(cell, false)
|
|
|
|
} else {
|
|
|
|
let cell = self.inner.next()?;
|
|
|
|
|
2018-03-07 04:57:40 +00:00
|
|
|
let index = Linear(cell.line.0 * self.grid.num_cols().0 + cell.column.0);
|
|
|
|
|
2018-02-16 02:35:49 +00:00
|
|
|
// XXX (jwilm) selection temp disabled
|
|
|
|
//
|
2018-03-07 04:57:40 +00:00
|
|
|
let selected = self.selection.as_ref()
|
|
|
|
.map(|range| range.contains_(index))
|
|
|
|
.unwrap_or(false);
|
2018-02-16 02:35:49 +00:00
|
|
|
|
|
|
|
// Skip empty cells
|
|
|
|
if cell.is_empty() && !selected {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
(cell, selected)
|
|
|
|
};
|
2016-11-28 22:13:11 +00:00
|
|
|
|
2018-02-16 02:35:49 +00:00
|
|
|
// Apply inversion and lookup RGB values
|
|
|
|
let mut bg_alpha = 1.0;
|
|
|
|
let fg_rgb;
|
|
|
|
let bg_rgb;
|
2017-05-28 16:38:10 +00:00
|
|
|
|
2018-02-16 02:35:49 +00:00
|
|
|
let invert = selected ^ cell.inverse();
|
2017-02-11 20:49:40 +00:00
|
|
|
|
2018-02-16 02:35:49 +00:00
|
|
|
if invert {
|
|
|
|
if cell.fg == cell.bg {
|
|
|
|
bg_rgb = self.colors[NamedColor::Foreground];
|
|
|
|
fg_rgb = self.colors[NamedColor::Background];
|
|
|
|
bg_alpha = 1.0
|
2017-06-19 00:04:16 +00:00
|
|
|
} else {
|
2018-02-16 02:35:49 +00:00
|
|
|
bg_rgb = self.compute_fg_rgb(&cell.fg, &cell);
|
|
|
|
fg_rgb = self.compute_bg_rgb(&cell.bg);
|
2017-10-21 22:26:42 +00:00
|
|
|
}
|
2018-02-16 02:35:49 +00:00
|
|
|
} else {
|
|
|
|
fg_rgb = self.compute_fg_rgb(&cell.fg, &cell);
|
|
|
|
bg_rgb = self.compute_bg_rgb(&cell.bg);
|
|
|
|
bg_alpha = self.compute_bg_alpha(&cell.bg);
|
2016-11-28 22:13:11 +00:00
|
|
|
}
|
|
|
|
|
2018-02-16 02:35:49 +00:00
|
|
|
return Some(RenderableCell {
|
|
|
|
line: cell.line,
|
|
|
|
column: cell.column,
|
|
|
|
flags: cell.flags,
|
|
|
|
c: cell.c,
|
|
|
|
fg: fg_rgb,
|
|
|
|
bg: bg_rgb,
|
|
|
|
bg_alpha: bg_alpha,
|
|
|
|
})
|
2016-11-28 22:13:11 +00:00
|
|
|
}
|
2016-07-04 21:46:25 +00:00
|
|
|
}
|
2017-05-28 16:38:10 +00:00
|
|
|
|
2016-07-04 21:46:25 +00:00
|
|
|
}
|
|
|
|
|
2016-06-08 04:17:48 +00:00
|
|
|
pub mod mode {
|
|
|
|
bitflags! {
|
2017-08-30 18:43:37 +00:00
|
|
|
pub struct TermMode: u16 {
|
2018-01-26 21:28:43 +00:00
|
|
|
const SHOW_CURSOR = 0b00_0000_0000_0001;
|
|
|
|
const APP_CURSOR = 0b00_0000_0000_0010;
|
|
|
|
const APP_KEYPAD = 0b00_0000_0000_0100;
|
|
|
|
const MOUSE_REPORT_CLICK = 0b00_0000_0000_1000;
|
|
|
|
const BRACKETED_PASTE = 0b00_0000_0001_0000;
|
|
|
|
const SGR_MOUSE = 0b00_0000_0010_0000;
|
|
|
|
const MOUSE_MOTION = 0b00_0000_0100_0000;
|
|
|
|
const LINE_WRAP = 0b00_0000_1000_0000;
|
|
|
|
const LINE_FEED_NEW_LINE = 0b00_0001_0000_0000;
|
|
|
|
const ORIGIN = 0b00_0010_0000_0000;
|
|
|
|
const INSERT = 0b00_0100_0000_0000;
|
|
|
|
const FOCUS_IN_OUT = 0b00_1000_0000_0000;
|
|
|
|
const ALT_SCREEN = 0b01_0000_0000_0000;
|
|
|
|
const MOUSE_DRAG = 0b10_0000_0000_0000;
|
|
|
|
const ANY = 0b11_1111_1111_1111;
|
2017-08-30 18:43:37 +00:00
|
|
|
const NONE = 0;
|
2016-06-23 16:42:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for TermMode {
|
|
|
|
fn default() -> TermMode {
|
2017-10-12 01:52:23 +00:00
|
|
|
TermMode::SHOW_CURSOR | TermMode::LINE_WRAP
|
2016-06-08 04:17:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub use self::mode::TermMode;
|
|
|
|
|
2017-01-09 20:07:23 +00:00
|
|
|
trait CharsetMapping {
|
|
|
|
fn map(&self, c: char) -> char {
|
|
|
|
c
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CharsetMapping for StandardCharset {
|
|
|
|
/// Switch/Map character to the active charset. Ascii is the common case and
|
|
|
|
/// for that we want to do as little as possible.
|
|
|
|
#[inline]
|
|
|
|
fn map(&self, c: char) -> char {
|
|
|
|
match *self {
|
|
|
|
StandardCharset::Ascii => c,
|
|
|
|
StandardCharset::SpecialCharacterAndLineDrawing =>
|
|
|
|
match c {
|
|
|
|
'`' => '◆',
|
|
|
|
'a' => '▒',
|
|
|
|
'b' => '\t',
|
|
|
|
'c' => '\u{000c}',
|
|
|
|
'd' => '\r',
|
|
|
|
'e' => '\n',
|
|
|
|
'f' => '°',
|
|
|
|
'g' => '±',
|
|
|
|
'h' => '\u{2424}',
|
|
|
|
'i' => '\u{000b}',
|
|
|
|
'j' => '┘',
|
|
|
|
'k' => '┐',
|
|
|
|
'l' => '┌',
|
|
|
|
'm' => '└',
|
|
|
|
'n' => '┼',
|
|
|
|
'o' => '⎺',
|
|
|
|
'p' => '⎻',
|
|
|
|
'q' => '─',
|
|
|
|
'r' => '⎼',
|
|
|
|
's' => '⎽',
|
|
|
|
't' => '├',
|
|
|
|
'u' => '┤',
|
|
|
|
'v' => '┴',
|
|
|
|
'w' => '┬',
|
|
|
|
'x' => '│',
|
|
|
|
'y' => '≤',
|
|
|
|
'z' => '≥',
|
|
|
|
'{' => 'π',
|
|
|
|
'|' => '≠',
|
|
|
|
'}' => '£',
|
|
|
|
'~' => '·',
|
|
|
|
_ => c
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
#[derive(Default, Copy, Clone)]
|
2017-01-09 20:07:23 +00:00
|
|
|
struct Charsets([StandardCharset; 4]);
|
|
|
|
|
|
|
|
impl Index<CharsetIndex> for Charsets {
|
|
|
|
type Output = StandardCharset;
|
|
|
|
fn index(&self, index: CharsetIndex) -> &StandardCharset {
|
|
|
|
&self.0[index as usize]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl IndexMut<CharsetIndex> for Charsets {
|
|
|
|
fn index_mut(&mut self, index: CharsetIndex) -> &mut StandardCharset {
|
|
|
|
&mut self.0[index as usize]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
#[derive(Default, Copy, Clone)]
|
|
|
|
pub struct Cursor {
|
|
|
|
/// The location of this cursor
|
2017-07-25 00:54:06 +00:00
|
|
|
pub point: Point,
|
2017-02-01 17:55:23 +00:00
|
|
|
|
|
|
|
/// Template cell when using this cursor
|
|
|
|
template: Cell,
|
|
|
|
|
|
|
|
/// Currently configured graphic character sets
|
|
|
|
charsets: Charsets,
|
|
|
|
}
|
|
|
|
|
2017-02-03 23:34:52 +00:00
|
|
|
pub struct VisualBell {
|
|
|
|
/// Visual bell animation
|
|
|
|
animation: VisualBellAnimation,
|
|
|
|
|
|
|
|
/// Visual bell duration
|
|
|
|
duration: Duration,
|
|
|
|
|
|
|
|
/// The last time the visual bell rang, if at all
|
|
|
|
start_time: Option<Instant>,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn cubic_bezier(p0: f64, p1: f64, p2: f64, p3: f64, x: f64) -> f64 {
|
|
|
|
(1.0 - x).powi(3) * p0 +
|
|
|
|
3.0 * (1.0 - x).powi(2) * x * p1 +
|
|
|
|
3.0 * (1.0 - x) * x.powi(2) * p2 +
|
|
|
|
x.powi(3) * p3
|
|
|
|
}
|
|
|
|
|
|
|
|
impl VisualBell {
|
|
|
|
pub fn new(config: &Config) -> VisualBell {
|
|
|
|
let visual_bell_config = config.visual_bell();
|
|
|
|
VisualBell {
|
|
|
|
animation: visual_bell_config.animation(),
|
|
|
|
duration: visual_bell_config.duration(),
|
|
|
|
start_time: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Ring the visual bell, and return its intensity.
|
|
|
|
pub fn ring(&mut self) -> f64 {
|
|
|
|
let now = Instant::now();
|
|
|
|
self.start_time = Some(now);
|
|
|
|
self.intensity_at_instant(now)
|
|
|
|
}
|
|
|
|
|
2017-10-30 15:03:58 +00:00
|
|
|
/// Get the currently intensity of the visual bell. The bell's intensity
|
2017-02-03 23:34:52 +00:00
|
|
|
/// ramps down from 1.0 to 0.0 at a rate determined by the bell's duration.
|
|
|
|
pub fn intensity(&self) -> f64 {
|
|
|
|
self.intensity_at_instant(Instant::now())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Check whether or not the visual bell has completed "ringing".
|
2017-02-20 20:22:26 +00:00
|
|
|
pub fn completed(&mut self) -> bool {
|
2017-02-03 23:34:52 +00:00
|
|
|
match self.start_time {
|
2017-02-20 20:22:26 +00:00
|
|
|
Some(earlier) => {
|
|
|
|
if Instant::now().duration_since(earlier) >= self.duration {
|
|
|
|
self.start_time = None;
|
|
|
|
}
|
|
|
|
false
|
|
|
|
},
|
2017-02-03 23:34:52 +00:00
|
|
|
None => true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the intensity of the visual bell at a particular instant. The bell's
|
|
|
|
/// intensity ramps down from 1.0 to 0.0 at a rate determined by the bell's
|
|
|
|
/// duration.
|
|
|
|
pub fn intensity_at_instant(&self, instant: Instant) -> f64 {
|
|
|
|
// If `duration` is zero, then the VisualBell is disabled; therefore,
|
|
|
|
// its `intensity` is zero.
|
|
|
|
if self.duration == Duration::from_secs(0) {
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
match self.start_time {
|
|
|
|
// Similarly, if `start_time` is `None`, then the VisualBell has not
|
|
|
|
// been "rung"; therefore, its `intensity` is zero.
|
|
|
|
None => 0.0,
|
|
|
|
|
|
|
|
Some(earlier) => {
|
|
|
|
// Finally, if the `instant` at which we wish to compute the
|
|
|
|
// VisualBell's `intensity` occurred before the VisualBell was
|
|
|
|
// "rung", then its `intensity` is also zero.
|
|
|
|
if instant < earlier {
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
let elapsed = instant.duration_since(earlier);
|
|
|
|
let elapsed_f = elapsed.as_secs() as f64 +
|
2018-01-06 01:42:55 +00:00
|
|
|
f64::from(elapsed.subsec_nanos()) / 1e9f64;
|
2017-02-03 23:34:52 +00:00
|
|
|
let duration_f = self.duration.as_secs() as f64 +
|
2018-01-06 01:42:55 +00:00
|
|
|
f64::from(self.duration.subsec_nanos()) / 1e9f64;
|
2017-02-03 23:34:52 +00:00
|
|
|
|
|
|
|
// Otherwise, we compute a value `time` from 0.0 to 1.0
|
|
|
|
// inclusive that represents the ratio of `elapsed` time to the
|
|
|
|
// `duration` of the VisualBell.
|
|
|
|
let time = (elapsed_f / duration_f).min(1.0);
|
|
|
|
|
|
|
|
// We use this to compute the inverse `intensity` of the
|
|
|
|
// VisualBell. When `time` is 0.0, `inverse_intensity` is 0.0,
|
|
|
|
// and when `time` is 1.0, `inverse_intensity` is 1.0.
|
|
|
|
let inverse_intensity = match self.animation {
|
2018-01-06 01:42:55 +00:00
|
|
|
VisualBellAnimation::Ease | VisualBellAnimation::EaseOut => {
|
|
|
|
cubic_bezier(0.25, 0.1, 0.25, 1.0, time)
|
|
|
|
},
|
2017-02-03 23:34:52 +00:00
|
|
|
VisualBellAnimation::EaseOutSine => cubic_bezier(0.39, 0.575, 0.565, 1.0, time),
|
|
|
|
VisualBellAnimation::EaseOutQuad => cubic_bezier(0.25, 0.46, 0.45, 0.94, time),
|
|
|
|
VisualBellAnimation::EaseOutCubic => cubic_bezier(0.215, 0.61, 0.355, 1.0, time),
|
|
|
|
VisualBellAnimation::EaseOutQuart => cubic_bezier(0.165, 0.84, 0.44, 1.0, time),
|
|
|
|
VisualBellAnimation::EaseOutQuint => cubic_bezier(0.23, 1.0, 0.32, 1.0, time),
|
|
|
|
VisualBellAnimation::EaseOutExpo => cubic_bezier(0.19, 1.0, 0.22, 1.0, time),
|
|
|
|
VisualBellAnimation::EaseOutCirc => cubic_bezier(0.075, 0.82, 0.165, 1.0, time),
|
|
|
|
VisualBellAnimation::Linear => time,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Since we want the `intensity` of the VisualBell to decay over
|
|
|
|
// `time`, we subtract the `inverse_intensity` from 1.0.
|
|
|
|
1.0 - inverse_intensity
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn update_config(&mut self, config: &Config) {
|
|
|
|
let visual_bell_config = config.visual_bell();
|
|
|
|
self.animation = visual_bell_config.animation();
|
|
|
|
self.duration = visual_bell_config.duration();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
pub struct Term {
|
|
|
|
/// The grid
|
2016-07-04 00:00:00 +00:00
|
|
|
grid: Grid<Cell>,
|
2016-05-31 03:44:37 +00:00
|
|
|
|
2017-01-12 03:00:29 +00:00
|
|
|
/// Tracks if the next call to input will need to first handle wrapping.
|
2017-01-08 03:46:02 +00:00
|
|
|
/// This is true after the last column is set with the input function. Any function that
|
|
|
|
/// implicitly sets the line or column needs to set this to false to avoid wrapping twice.
|
|
|
|
/// input_needs_wrap ensures that cursor.col is always valid for use into indexing into
|
2017-10-30 15:03:58 +00:00
|
|
|
/// arrays. Without it we would have to sanitize cursor.col every time we used it.
|
2017-01-12 03:00:29 +00:00
|
|
|
input_needs_wrap: bool,
|
2017-01-07 23:13:42 +00:00
|
|
|
|
2017-01-11 05:21:19 +00:00
|
|
|
/// Got a request to set title; it's buffered here until next draw.
|
|
|
|
///
|
|
|
|
/// Would be nice to avoid the allocation...
|
|
|
|
next_title: Option<String>,
|
|
|
|
|
2017-12-24 20:15:42 +00:00
|
|
|
/// Got a request to set the mouse cursor; it's buffered here until the next draw
|
|
|
|
next_mouse_cursor: Option<MouseCursor>,
|
|
|
|
|
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
|
2017-02-01 17:55:23 +00:00
|
|
|
cursor: Cursor,
|
2017-01-09 20:07:23 +00:00
|
|
|
|
|
|
|
/// The graphic character set, out of `charsets`, which ASCII is currently
|
|
|
|
/// being mapped to
|
|
|
|
active_charset: CharsetIndex,
|
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
/// 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
|
|
|
|
2018-01-05 03:22:58 +00:00
|
|
|
/// Font size
|
|
|
|
pub font_size: Size,
|
|
|
|
original_font_size: Size,
|
2017-10-14 17:35:56 +00:00
|
|
|
|
2016-06-28 16:18:54 +00:00
|
|
|
/// Size
|
|
|
|
size_info: SizeInfo,
|
2016-07-30 03:16:13 +00:00
|
|
|
|
2016-09-01 17:24:20 +00:00
|
|
|
pub dirty: bool,
|
2017-01-09 03:18:39 +00:00
|
|
|
|
2017-02-03 23:34:52 +00:00
|
|
|
pub visual_bell: VisualBell,
|
2017-10-21 23:03:58 +00:00
|
|
|
pub next_is_urgent: Option<bool>,
|
2017-02-03 23:34:52 +00:00
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
/// Saved cursor from main grid
|
|
|
|
cursor_save: Cursor,
|
|
|
|
|
|
|
|
/// Saved cursor from alt grid
|
|
|
|
cursor_save_alt: Cursor,
|
2017-01-15 01:53:48 +00:00
|
|
|
|
|
|
|
semantic_escape_chars: String,
|
2017-02-11 20:49:40 +00:00
|
|
|
|
|
|
|
/// Colors used for rendering
|
2017-08-29 15:54:12 +00:00
|
|
|
colors: color::List,
|
|
|
|
|
|
|
|
/// Is color in `colors` modified or not
|
|
|
|
color_modified: [bool; color::COUNT],
|
2017-07-28 22:14:18 +00:00
|
|
|
|
|
|
|
/// Original colors from config
|
|
|
|
original_colors: color::List,
|
2017-05-28 16:38:10 +00:00
|
|
|
|
2017-12-03 21:38:42 +00:00
|
|
|
/// Current style of the cursor
|
|
|
|
cursor_style: Option<CursorStyle>,
|
|
|
|
|
|
|
|
/// Default style for resetting the cursor
|
|
|
|
default_cursor_style: CursorStyle,
|
2017-12-24 18:24:28 +00:00
|
|
|
|
|
|
|
dynamic_title: bool,
|
2018-01-06 00:50:12 +00:00
|
|
|
|
|
|
|
/// Number of spaces in one tab
|
|
|
|
tabspaces: usize,
|
2018-03-23 00:01:55 +00:00
|
|
|
|
|
|
|
/// Automatically scroll to bottom when new lines are added
|
|
|
|
auto_scroll: 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,
|
2017-05-06 15:45:23 +00:00
|
|
|
|
|
|
|
/// Horizontal window padding
|
|
|
|
pub padding_x: f32,
|
|
|
|
|
|
|
|
/// Horizontal window padding
|
|
|
|
pub padding_y: f32,
|
2016-06-28 16:18:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl SizeInfo {
|
|
|
|
#[inline]
|
2016-07-04 00:00:00 +00:00
|
|
|
pub fn lines(&self) -> Line {
|
2017-05-06 15:45:23 +00:00
|
|
|
Line(((self.height - 2. * self.padding_y) / 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 {
|
2017-05-06 15:45:23 +00:00
|
|
|
Column(((self.width - 2. * self.padding_x) / self.cell_width) as usize)
|
|
|
|
}
|
|
|
|
|
2018-03-13 18:00:14 +00:00
|
|
|
pub fn contains_point(&self, x: usize, y:usize) -> bool {
|
2017-05-07 00:37:01 +00:00
|
|
|
x <= (self.width - self.padding_x) as usize &&
|
|
|
|
x >= self.padding_x as usize &&
|
|
|
|
y <= (self.height - self.padding_y) as usize &&
|
2017-05-06 15:45:23 +00:00
|
|
|
y >= self.padding_y as usize
|
2016-06-28 16:18:54 +00:00
|
|
|
}
|
2016-12-12 06:02:03 +00:00
|
|
|
|
2018-03-13 18:00:14 +00:00
|
|
|
pub fn pixels_to_coords(&self, x: usize, y: usize) -> Point {
|
|
|
|
let col = Column(x.saturating_sub(self.padding_x as usize) / (self.cell_width as usize));
|
|
|
|
let line = Line(y.saturating_sub(self.padding_y as usize) / (self.cell_height as usize));
|
2016-12-12 06:02:03 +00:00
|
|
|
|
2018-03-13 18:00:14 +00:00
|
|
|
Point {
|
2017-01-08 03:46:02 +00:00
|
|
|
line: min(line, self.lines() - 1),
|
|
|
|
col: min(col, self.cols() - 1)
|
2018-03-13 18:00:14 +00:00
|
|
|
}
|
2016-12-12 06:02:03 +00:00
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
2017-02-11 20:49:40 +00:00
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
impl Term {
|
2018-03-05 17:57:34 +00:00
|
|
|
pub fn selection(&self) -> &Option<Selection> {
|
|
|
|
&self.grid.selection
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn selection_mut(&mut self) -> &mut Option<Selection> {
|
|
|
|
&mut self.grid.selection
|
|
|
|
}
|
|
|
|
|
2017-01-11 05:21:19 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn get_next_title(&mut self) -> Option<String> {
|
|
|
|
self.next_title.take()
|
|
|
|
}
|
|
|
|
|
2018-03-11 12:01:06 +00:00
|
|
|
pub fn scroll_display(&mut self, scroll: Scroll) {
|
|
|
|
self.grid.scroll_display(scroll);
|
2018-03-10 19:24:10 +00:00
|
|
|
self.dirty = true;
|
2018-02-17 01:33:32 +00:00
|
|
|
}
|
|
|
|
|
2017-12-24 20:15:42 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn get_next_mouse_cursor(&mut self) -> Option<MouseCursor> {
|
|
|
|
self.next_mouse_cursor.take()
|
|
|
|
}
|
|
|
|
|
2017-12-03 21:38:42 +00:00
|
|
|
pub fn new(config: &Config, size: SizeInfo) -> Term {
|
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
|
|
|
|
2018-03-09 18:45:40 +00:00
|
|
|
let history_size = config.scrolling().history as usize;
|
2018-04-28 14:14:45 +00:00
|
|
|
let grid = Grid::new(num_lines, num_cols, history_size, Cell::default());
|
2016-06-28 16:18:54 +00:00
|
|
|
|
2018-01-06 00:50:12 +00:00
|
|
|
let tabspaces = config.tabspaces();
|
2017-06-15 19:46:53 +00:00
|
|
|
let tabs = IndexRange::from(Column(0)..grid.num_cols())
|
2018-01-06 00:50:12 +00:00
|
|
|
.map(|i| (*i as usize) % tabspaces == 0)
|
2016-07-30 03:16:13 +00:00
|
|
|
.collect::<Vec<bool>>();
|
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
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 {
|
2017-01-11 05:21:19 +00:00
|
|
|
next_title: None,
|
2017-12-24 20:15:42 +00:00
|
|
|
next_mouse_cursor: None,
|
2016-12-30 02:38:22 +00:00
|
|
|
dirty: false,
|
2017-02-03 23:34:52 +00:00
|
|
|
visual_bell: VisualBell::new(config),
|
2017-10-21 23:03:58 +00:00
|
|
|
next_is_urgent: None,
|
2017-01-12 03:00:29 +00:00
|
|
|
input_needs_wrap: false,
|
2018-03-04 22:40:15 +00:00
|
|
|
grid,
|
2016-05-31 03:44:37 +00:00
|
|
|
alt_grid: alt,
|
|
|
|
alt: false,
|
2018-01-05 03:22:58 +00:00
|
|
|
font_size: config.font().size(),
|
|
|
|
original_font_size: config.font().size(),
|
2017-02-01 17:55:23 +00:00
|
|
|
active_charset: Default::default(),
|
|
|
|
cursor: Default::default(),
|
|
|
|
cursor_save: Default::default(),
|
|
|
|
cursor_save_alt: Default::default(),
|
2018-03-04 22:40:15 +00:00
|
|
|
tabs,
|
2016-06-23 16:42:00 +00:00
|
|
|
mode: Default::default(),
|
2018-03-04 22:40:15 +00:00
|
|
|
scroll_region,
|
2016-07-30 03:16:13 +00:00
|
|
|
size_info: size,
|
2017-02-11 20:49:40 +00:00
|
|
|
colors: color::List::from(config.colors()),
|
2017-08-29 15:54:12 +00:00
|
|
|
color_modified: [false; color::COUNT],
|
2017-07-28 22:14:18 +00:00
|
|
|
original_colors: color::List::from(config.colors()),
|
2017-01-15 01:53:48 +00:00
|
|
|
semantic_escape_chars: config.selection().semantic_escape_chars.clone(),
|
2017-12-03 21:38:42 +00:00
|
|
|
cursor_style: None,
|
|
|
|
default_cursor_style: config.cursor_style(),
|
2017-12-24 18:24:28 +00:00
|
|
|
dynamic_title: config.dynamic_title(),
|
2018-01-06 00:50:12 +00:00
|
|
|
tabspaces,
|
2018-03-23 00:01:55 +00:00
|
|
|
auto_scroll: config.scrolling().auto_scroll,
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-14 17:35:56 +00:00
|
|
|
pub fn change_font_size(&mut self, delta: i8) {
|
2018-01-05 03:22:58 +00:00
|
|
|
// Saturating addition with minimum font size 1
|
2018-01-06 01:42:55 +00:00
|
|
|
let new_size = self.font_size + Size::new(f32::from(delta));
|
2018-01-05 03:22:58 +00:00
|
|
|
self.font_size = max(new_size, Size::new(1.));
|
|
|
|
self.dirty = true;
|
2017-10-14 17:35:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn reset_font_size(&mut self) {
|
2018-01-05 03:22:58 +00:00
|
|
|
self.font_size = self.original_font_size;
|
2017-10-14 17:35:56 +00:00
|
|
|
self.dirty = true;
|
|
|
|
}
|
|
|
|
|
2017-01-09 03:18:39 +00:00
|
|
|
pub fn update_config(&mut self, config: &Config) {
|
2017-01-15 01:53:48 +00:00
|
|
|
self.semantic_escape_chars = config.selection().semantic_escape_chars.clone();
|
2017-07-28 22:14:18 +00:00
|
|
|
self.original_colors.fill_named(config.colors());
|
2017-08-29 15:54:12 +00:00
|
|
|
for i in 0..color::COUNT {
|
|
|
|
if !self.color_modified[i] {
|
|
|
|
self.colors[i] = self.original_colors[i];
|
|
|
|
}
|
|
|
|
}
|
2017-02-03 23:34:52 +00:00
|
|
|
self.visual_bell.update_config(config);
|
2017-12-03 21:38:42 +00:00
|
|
|
self.default_cursor_style = config.cursor_style();
|
2017-12-24 18:24:28 +00:00
|
|
|
self.dynamic_title = config.dynamic_title();
|
2018-03-23 00:01:55 +00:00
|
|
|
self.auto_scroll = config.scrolling().auto_scroll;
|
2018-05-30 08:20:47 +00:00
|
|
|
self.grid
|
|
|
|
.update_history(config.scrolling().history as usize, &self.cursor.template);
|
2017-01-09 03:18:39 +00:00
|
|
|
}
|
|
|
|
|
2016-12-30 02:38:22 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn needs_draw(&self) -> bool {
|
|
|
|
self.dirty
|
|
|
|
}
|
|
|
|
|
2018-03-05 17:57:34 +00:00
|
|
|
pub fn selection_to_string(&self) -> Option<String> {
|
2016-12-29 15:43:58 +00:00
|
|
|
/// Need a generic push() for the Append trait
|
|
|
|
trait PushChar {
|
|
|
|
fn push_char(&mut self, c: char);
|
2018-03-07 04:57:40 +00:00
|
|
|
fn maybe_newline(&mut self, grid: &Grid<Cell>, line: usize, ending: Column) {
|
2017-10-12 01:52:23 +00:00
|
|
|
if ending != Column(0) && !grid[line][ending - 1].flags.contains(cell::Flags::WRAPLINE) {
|
2016-12-29 15:43:58 +00:00
|
|
|
self.push_char('\n');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PushChar for String {
|
|
|
|
#[inline]
|
|
|
|
fn push_char(&mut self, c: char) {
|
|
|
|
self.push(c);
|
|
|
|
}
|
|
|
|
}
|
2016-12-27 03:52:37 +00:00
|
|
|
|
2017-12-23 05:17:04 +00:00
|
|
|
use std::ops::Range;
|
|
|
|
|
|
|
|
trait Append : PushChar {
|
2018-03-07 04:57:40 +00:00
|
|
|
fn append(&mut self, grid: &Grid<Cell>, line: usize, cols: Range<Column>) -> Option<Range<Column>>;
|
2017-12-23 05:17:04 +00:00
|
|
|
}
|
2016-12-27 03:52:37 +00:00
|
|
|
|
2017-12-23 05:17:04 +00:00
|
|
|
impl Append for String {
|
2016-12-30 01:39:30 +00:00
|
|
|
fn append(
|
|
|
|
&mut self,
|
|
|
|
grid: &Grid<Cell>,
|
2018-03-07 04:57:40 +00:00
|
|
|
line: usize,
|
2016-12-30 01:39:30 +00:00
|
|
|
cols: Range<Column>
|
|
|
|
) -> Option<Range<Column>> {
|
2017-12-23 05:17:04 +00:00
|
|
|
let grid_line = &grid[line];
|
|
|
|
let line_length = grid_line.line_length();
|
2017-01-08 03:46:02 +00:00
|
|
|
let line_end = min(line_length, cols.end + 1);
|
2016-12-29 15:43:58 +00:00
|
|
|
|
2016-12-30 01:39:30 +00:00
|
|
|
if cols.start >= line_end {
|
|
|
|
None
|
|
|
|
} else {
|
2017-12-23 05:17:04 +00:00
|
|
|
for cell in &grid_line[cols.start..line_end] {
|
2017-10-12 01:52:23 +00:00
|
|
|
if !cell.flags.contains(cell::Flags::WIDE_CHAR_SPACER) {
|
2017-03-02 06:24:37 +00:00
|
|
|
self.push(cell.c);
|
|
|
|
}
|
2016-12-30 01:39:30 +00:00
|
|
|
}
|
|
|
|
|
2017-12-23 05:17:04 +00:00
|
|
|
let range = Some(cols.start..line_end);
|
|
|
|
if cols.end >= grid.num_cols() - 1 {
|
2018-05-11 18:22:36 +00:00
|
|
|
if let Some(ref range) = range {
|
|
|
|
self.maybe_newline(grid, line, range.end);
|
|
|
|
}
|
2017-12-23 05:17:04 +00:00
|
|
|
}
|
2016-12-27 03:52:37 +00:00
|
|
|
|
2017-12-23 05:17:04 +00:00
|
|
|
range
|
|
|
|
}
|
2016-12-27 03:52:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-05 17:57:34 +00:00
|
|
|
let selection = self.grid.selection.clone()?;
|
|
|
|
let span = selection.to_span(self)?;
|
|
|
|
|
2016-12-27 03:52:37 +00:00
|
|
|
let mut res = String::new();
|
|
|
|
|
2018-03-07 04:57:40 +00:00
|
|
|
let Locations { mut start, mut end } = span.to_locations();
|
|
|
|
|
|
|
|
if start > end {
|
|
|
|
::std::mem::swap(&mut start, &mut end);
|
|
|
|
}
|
|
|
|
|
2016-12-27 03:52:37 +00:00
|
|
|
let line_count = end.line - start.line;
|
2017-12-23 05:17:04 +00:00
|
|
|
let max_col = Column(usize::max_value() - 1);
|
2016-12-27 03:52:37 +00:00
|
|
|
|
|
|
|
match line_count {
|
|
|
|
// Selection within single line
|
2018-03-07 04:57:40 +00:00
|
|
|
0 => {
|
2016-12-27 03:52:37 +00:00
|
|
|
res.append(&self.grid, start.line, start.col..end.col);
|
|
|
|
},
|
|
|
|
|
|
|
|
// Selection ends on line following start
|
2018-03-07 04:57:40 +00:00
|
|
|
1 => {
|
|
|
|
// Ending line
|
|
|
|
res.append(&self.grid, end.line, end.col..max_col);
|
|
|
|
|
2016-12-27 03:52:37 +00:00
|
|
|
// Starting line
|
2018-03-07 04:57:40 +00:00
|
|
|
res.append(&self.grid, start.line, Column(0)..start.col);
|
2016-12-27 03:52:37 +00:00
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
// Multi line selection
|
|
|
|
_ => {
|
2018-03-07 04:57:40 +00:00
|
|
|
// Ending line
|
|
|
|
res.append(&self.grid, end.line, end.col..max_col);
|
2016-12-27 03:52:37 +00:00
|
|
|
|
2018-03-07 04:57:40 +00:00
|
|
|
let middle_range = (start.line + 1)..(end.line);
|
2018-03-09 04:12:48 +00:00
|
|
|
for line in middle_range.rev() {
|
2017-12-23 05:17:04 +00:00
|
|
|
res.append(&self.grid, line, Column(0)..max_col);
|
2016-12-27 03:52:37 +00:00
|
|
|
}
|
|
|
|
|
2018-03-07 04:57:40 +00:00
|
|
|
// Starting line
|
|
|
|
res.append(&self.grid, start.line, Column(0)..(start.col + 1));
|
|
|
|
|
2016-12-27 03:52:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-05 17:57:34 +00:00
|
|
|
Some(res)
|
2016-12-27 03:52:37 +00:00
|
|
|
}
|
|
|
|
|
2018-03-07 04:57:40 +00:00
|
|
|
pub(crate) fn visible_to_buffer(&self, point: Point) -> Point<usize> {
|
|
|
|
self.grid.visible_to_buffer(point)
|
|
|
|
}
|
|
|
|
|
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
|
2016-12-30 01:39:30 +00:00
|
|
|
pub fn pixels_to_coords(&self, x: usize, y: usize) -> Option<Point> {
|
2018-03-13 18:00:14 +00:00
|
|
|
if self.size_info.contains_point(x, y) {
|
|
|
|
Some(self.size_info.pixels_to_coords(x, y))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
2016-11-24 04:25:37 +00:00
|
|
|
}
|
|
|
|
|
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.
|
2017-02-11 20:49:40 +00:00
|
|
|
pub fn renderable_cells<'b>(
|
2017-05-28 16:38:10 +00:00
|
|
|
&'b self,
|
2017-02-11 20:49:40 +00:00
|
|
|
config: &'b Config,
|
2017-12-09 23:33:58 +00:00
|
|
|
window_focused: bool,
|
2017-02-11 20:49:40 +00:00
|
|
|
) -> RenderableCellsIter {
|
2018-03-07 04:57:40 +00:00
|
|
|
let selection = self.grid.selection.as_ref()
|
|
|
|
.and_then(|s| s.to_span(self))
|
|
|
|
.map(|span| {
|
|
|
|
span.to_locations()
|
|
|
|
});
|
2018-01-06 01:42:55 +00:00
|
|
|
let cursor = if window_focused {
|
|
|
|
self.cursor_style.unwrap_or(self.default_cursor_style)
|
|
|
|
} else {
|
|
|
|
CursorStyle::HollowBlock
|
|
|
|
};
|
2017-06-16 04:43:28 +00:00
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
RenderableCellsIter::new(
|
2017-05-28 16:38:10 +00:00
|
|
|
&self.grid,
|
2017-02-01 17:55:23 +00:00
|
|
|
&self.cursor.point,
|
2017-02-11 20:49:40 +00:00
|
|
|
&self.colors,
|
2017-02-01 17:55:23 +00:00
|
|
|
self.mode,
|
2017-02-11 20:49:40 +00:00
|
|
|
config,
|
2017-02-01 17:55:23 +00:00
|
|
|
selection,
|
2018-01-06 01:42:55 +00:00
|
|
|
cursor,
|
2017-02-01 17:55:23 +00:00
|
|
|
)
|
2016-07-04 21:46:25 +00:00
|
|
|
}
|
|
|
|
|
2016-06-29 17:21:02 +00:00
|
|
|
/// Resize terminal to new dimensions
|
2017-10-14 17:35:56 +00:00
|
|
|
pub fn resize(&mut self, size : &SizeInfo) {
|
2017-05-06 15:45:23 +00:00
|
|
|
debug!("Term::resize");
|
2017-10-14 17:35:56 +00:00
|
|
|
|
2017-05-06 15:45:23 +00:00
|
|
|
// Bounds check; lots of math assumes width and height are > 0
|
2017-10-14 17:35:56 +00:00
|
|
|
if size.width as usize <= 2 * self.size_info.padding_x as usize ||
|
|
|
|
size.height as usize <= 2 * self.size_info.padding_y as usize
|
2017-05-06 15:45:23 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let old_cols = self.grid.num_cols();
|
|
|
|
let old_lines = self.grid.num_lines();
|
2017-01-08 00:07:03 +00:00
|
|
|
let mut num_cols = size.cols();
|
|
|
|
let mut num_lines = size.lines();
|
2016-06-29 17:21:02 +00:00
|
|
|
|
2017-10-14 17:35:56 +00:00
|
|
|
self.size_info = *size;
|
2016-06-29 17:21:02 +00:00
|
|
|
|
2016-07-04 00:00:00 +00:00
|
|
|
if old_cols == num_cols && old_lines == num_lines {
|
2017-05-06 15:45:23 +00:00
|
|
|
debug!("Term::resize dimensions unchanged");
|
2016-06-29 17:21:02 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-03-05 17:57:34 +00:00
|
|
|
self.grid.selection = None;
|
|
|
|
self.alt_grid.selection = None;
|
|
|
|
|
2017-01-12 03:00:29 +00:00
|
|
|
// Should not allow less than 1 col, causes all sorts of checks to be required.
|
2017-01-08 00:07:03 +00:00
|
|
|
if num_cols <= Column(1) {
|
2017-01-12 03:00:29 +00:00
|
|
|
num_cols = Column(2);
|
2017-01-08 00:07:03 +00:00
|
|
|
}
|
|
|
|
|
2017-01-12 03:00:29 +00:00
|
|
|
// Should not allow less than 1 line, causes all sorts of checks to be required.
|
2017-01-08 00:07:03 +00:00
|
|
|
if num_lines <= Line(1) {
|
2017-01-12 03:00:29 +00:00
|
|
|
num_lines = Line(2);
|
2017-01-08 00:07:03 +00:00
|
|
|
}
|
|
|
|
|
2016-07-04 00:00:00 +00:00
|
|
|
// Scroll up to keep cursor in terminal
|
2017-02-01 17:55:23 +00:00
|
|
|
if self.cursor.point.line >= num_lines {
|
|
|
|
let lines = self.cursor.point.line - num_lines + 1;
|
2018-04-02 15:44:54 +00:00
|
|
|
self.grid.scroll_up(&(Line(0)..old_lines), lines, &self.cursor.template);
|
2017-04-20 17:13:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Scroll up alt grid as well
|
|
|
|
if self.cursor_save_alt.point.line >= num_lines {
|
|
|
|
let lines = self.cursor_save_alt.point.line - num_lines + 1;
|
2018-04-02 15:44:54 +00:00
|
|
|
self.alt_grid.scroll_up(&(Line(0)..old_lines), lines, &self.cursor_save_alt.template);
|
2016-06-29 17:21:02 +00:00
|
|
|
}
|
2017-05-12 10:32:15 +00:00
|
|
|
debug!("num_cols, num_lines = {}, {}", num_cols, num_lines);
|
2016-06-29 17:21:02 +00:00
|
|
|
|
|
|
|
// Resize grids to new size
|
2018-04-02 15:44:54 +00:00
|
|
|
self.grid.resize(num_lines, num_cols, &self.cursor.template);
|
|
|
|
self.alt_grid.resize(num_lines, num_cols, &self.cursor_save_alt.template);
|
2016-06-29 17:21:02 +00:00
|
|
|
|
2017-04-20 17:13:28 +00:00
|
|
|
// Reset scrolling region to new size
|
|
|
|
self.scroll_region = Line(0)..self.grid.num_lines();
|
|
|
|
|
|
|
|
// Ensure cursors are in-bounds.
|
|
|
|
self.cursor.point.col = min(self.cursor.point.col, num_cols - 1);
|
|
|
|
self.cursor.point.line = min(self.cursor.point.line, num_lines - 1);
|
|
|
|
self.cursor_save.point.col = min(self.cursor_save.point.col, num_cols - 1);
|
|
|
|
self.cursor_save.point.line = min(self.cursor_save.point.line, num_lines - 1);
|
|
|
|
self.cursor_save_alt.point.col = min(self.cursor_save_alt.point.col, num_cols - 1);
|
|
|
|
self.cursor_save_alt.point.line = min(self.cursor_save_alt.point.line, num_lines - 1);
|
2016-06-29 17:21:02 +00:00
|
|
|
|
|
|
|
// Recreate tabs list
|
2017-01-06 23:25:04 +00:00
|
|
|
self.tabs = IndexRange::from(Column(0)..self.grid.num_cols())
|
2018-01-06 00:50:12 +00:00
|
|
|
.map(|i| (*i as usize) % self.tabspaces == 0)
|
2016-12-17 06:48:04 +00:00
|
|
|
.collect::<Vec<bool>>();
|
2016-06-29 17:21:02 +00:00
|
|
|
|
2018-02-16 02:35:49 +00:00
|
|
|
// if num_lines > old_lines {
|
|
|
|
// // Make sure bottom of terminal is clear
|
|
|
|
// let template = self.cursor.template;
|
|
|
|
// self.grid
|
|
|
|
// .region_mut((self.cursor.point.line + 1)..)
|
|
|
|
// .each(|c| c.reset(&template));
|
|
|
|
// self.alt_grid
|
|
|
|
// .region_mut((self.cursor_save_alt.point.line + 1)..)
|
|
|
|
// .each(|c| c.reset(&template));
|
|
|
|
// }
|
2016-06-29 17:21:02 +00:00
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2017-07-25 00:54:06 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn cursor(&self) -> &Cursor {
|
|
|
|
&self.cursor
|
|
|
|
}
|
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
pub fn swap_alt(&mut self) {
|
|
|
|
if self.alt {
|
2017-08-09 18:22:07 +00:00
|
|
|
let template = &self.cursor.template;
|
2017-10-13 03:12:29 +00:00
|
|
|
self.grid.region_mut(..).each(|c| c.reset(template));
|
2016-06-08 17:39:49 +00:00
|
|
|
}
|
2017-02-03 04:51:14 +00:00
|
|
|
|
|
|
|
self.alt = !self.alt;
|
|
|
|
::std::mem::swap(&mut self.grid, &mut self.alt_grid);
|
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
|
2017-01-10 06:05:26 +00:00
|
|
|
/// Expects origin to be in scroll range.
|
2016-08-22 15:37:50 +00:00
|
|
|
#[inline]
|
|
|
|
fn scroll_down_relative(&mut self, origin: Line, lines: Line) {
|
2017-05-01 04:32:11 +00:00
|
|
|
trace!("scroll_down_relative: origin={}, lines={}", origin, lines);
|
|
|
|
let lines = min(lines, self.scroll_region.end - self.scroll_region.start);
|
2016-08-22 15:37:50 +00:00
|
|
|
|
|
|
|
// Scroll between origin and bottom
|
2018-04-02 15:44:54 +00:00
|
|
|
self.grid.scroll_down(&(origin..self.scroll_region.end), lines, &self.cursor.template);
|
2016-08-22 15:37:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Scroll screen up
|
|
|
|
///
|
|
|
|
/// Text moves up; clear at top
|
2017-01-10 06:05:26 +00:00
|
|
|
/// Expects origin to be in scroll range.
|
2016-08-22 15:37:50 +00:00
|
|
|
#[inline]
|
|
|
|
fn scroll_up_relative(&mut self, origin: Line, lines: Line) {
|
2017-05-01 04:32:11 +00:00
|
|
|
trace!("scroll_up_relative: origin={}, lines={}", origin, lines);
|
|
|
|
let lines = min(lines, self.scroll_region.end - self.scroll_region.start);
|
2016-08-22 15:37:50 +00:00
|
|
|
|
|
|
|
// Scroll from origin to bottom less number of lines
|
2018-04-02 15:44:54 +00:00
|
|
|
self.grid.scroll_up(&(origin..self.scroll_region.end), lines, &self.cursor.template);
|
2016-08-22 15:37:50 +00:00
|
|
|
}
|
2017-04-19 16:03:33 +00:00
|
|
|
|
|
|
|
fn deccolm(&mut self) {
|
|
|
|
// Setting 132 column font makes no sense, but run the other side effects
|
|
|
|
// Clear scrolling region
|
|
|
|
let scroll_region = Line(0)..self.grid.num_lines();
|
|
|
|
self.set_scrolling_region(scroll_region);
|
|
|
|
|
|
|
|
// Clear grid
|
2017-08-09 18:22:07 +00:00
|
|
|
let template = self.cursor.template;
|
2017-10-13 03:12:29 +00:00
|
|
|
self.grid.region_mut(..).each(|c| c.reset(&template));
|
2017-04-19 16:03:33 +00:00
|
|
|
}
|
2017-07-28 22:14:18 +00:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn background_color(&self) -> Rgb {
|
|
|
|
self.colors[NamedColor::Background]
|
|
|
|
}
|
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 {
|
2017-01-11 05:21:19 +00:00
|
|
|
/// Set the window title
|
|
|
|
#[inline]
|
|
|
|
fn set_title(&mut self, title: &str) {
|
2017-12-24 18:24:28 +00:00
|
|
|
if self.dynamic_title {
|
|
|
|
self.next_title = Some(title.to_owned());
|
|
|
|
}
|
2017-01-11 05:21:19 +00:00
|
|
|
}
|
|
|
|
|
2017-12-24 20:15:42 +00:00
|
|
|
/// Set the mouse cursor
|
|
|
|
#[inline]
|
|
|
|
fn set_mouse_cursor(&mut self, cursor: MouseCursor) {
|
|
|
|
self.next_mouse_cursor = Some(cursor);
|
|
|
|
}
|
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
/// A character to be displayed
|
|
|
|
#[inline]
|
|
|
|
fn input(&mut self, c: char) {
|
2018-03-23 00:01:55 +00:00
|
|
|
// If enabled, scroll to bottom when character is received
|
|
|
|
if self.auto_scroll {
|
|
|
|
self.scroll_display(Scroll::Bottom);
|
|
|
|
}
|
|
|
|
|
2017-01-08 03:46:02 +00:00
|
|
|
if self.input_needs_wrap {
|
2017-10-12 01:52:23 +00:00
|
|
|
if !self.mode.contains(mode::TermMode::LINE_WRAP) {
|
2017-01-12 03:00:29 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("wrapping");
|
2017-01-12 03:00:29 +00:00
|
|
|
|
2016-12-29 15:43:58 +00:00
|
|
|
{
|
2016-12-29 16:09:29 +00:00
|
|
|
let location = Point {
|
2017-02-01 17:55:23 +00:00
|
|
|
line: self.cursor.point.line,
|
|
|
|
col: self.cursor.point.col
|
2016-12-29 15:43:58 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let cell = &mut self.grid[&location];
|
2017-10-12 01:52:23 +00:00
|
|
|
cell.flags.insert(cell::Flags::WRAPLINE);
|
2016-12-29 15:43:58 +00:00
|
|
|
}
|
2017-01-12 03:00:29 +00:00
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
if (self.cursor.point.line + 1) >= self.scroll_region.end {
|
2016-08-20 00:55:44 +00:00
|
|
|
self.linefeed();
|
|
|
|
} else {
|
2017-02-01 17:55:23 +00:00
|
|
|
self.cursor.point.line += 1;
|
2016-08-20 00:55:44 +00:00
|
|
|
}
|
2017-01-12 03:00:29 +00:00
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
self.cursor.point.col = Column(0);
|
2017-01-08 03:46:02 +00:00
|
|
|
self.input_needs_wrap = false;
|
2016-07-30 03:16:13 +00:00
|
|
|
}
|
|
|
|
|
2017-01-07 21:47:05 +00:00
|
|
|
{
|
2017-03-02 06:24:37 +00:00
|
|
|
// Number of cells the char will occupy
|
2017-05-01 05:10:38 +00:00
|
|
|
if let Some(width) = c.width() {
|
|
|
|
// Sigh, borrowck making us check the width twice. Hopefully the
|
|
|
|
// optimizer can fix it.
|
|
|
|
let num_cols = self.grid.num_cols();
|
|
|
|
{
|
|
|
|
// If in insert mode, first shift cells to the right.
|
2017-10-12 01:52:23 +00:00
|
|
|
if self.mode.contains(mode::TermMode::INSERT) && self.cursor.point.col + width < num_cols {
|
2017-05-01 05:10:38 +00:00
|
|
|
let line = self.cursor.point.line; // borrowck
|
|
|
|
let col = self.cursor.point.col;
|
|
|
|
let line = &mut self.grid[line];
|
|
|
|
|
|
|
|
let src = line[col..].as_ptr();
|
|
|
|
let dst = line[(col + width)..].as_mut_ptr();
|
|
|
|
unsafe {
|
|
|
|
// memmove
|
|
|
|
ptr::copy(src, dst, (num_cols - col - width).0);
|
|
|
|
}
|
|
|
|
}
|
2017-03-02 06:24:37 +00:00
|
|
|
|
2017-05-01 05:10:38 +00:00
|
|
|
let cell = &mut self.grid[&self.cursor.point];
|
|
|
|
*cell = self.cursor.template;
|
|
|
|
cell.c = self.cursor.charsets[self.active_charset].map(c);
|
2017-03-02 06:24:37 +00:00
|
|
|
|
2017-05-01 05:10:38 +00:00
|
|
|
// Handle wide chars
|
|
|
|
if width == 2 {
|
2017-10-12 01:52:23 +00:00
|
|
|
cell.flags.insert(cell::Flags::WIDE_CHAR);
|
2017-05-01 05:10:38 +00:00
|
|
|
}
|
2017-03-02 06:24:37 +00:00
|
|
|
}
|
|
|
|
|
2017-05-01 05:10:38 +00:00
|
|
|
// Set spacer cell for wide chars.
|
2018-01-06 01:42:55 +00:00
|
|
|
if width == 2 && self.cursor.point.col + 1 < num_cols {
|
|
|
|
self.cursor.point.col += 1;
|
|
|
|
let spacer = &mut self.grid[&self.cursor.point];
|
|
|
|
*spacer = self.cursor.template;
|
|
|
|
spacer.flags.insert(cell::Flags::WIDE_CHAR_SPACER);
|
2017-03-02 06:24:37 +00:00
|
|
|
}
|
|
|
|
}
|
2017-01-12 03:00:29 +00:00
|
|
|
}
|
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
if (self.cursor.point.col + 1) < self.grid.num_cols() {
|
|
|
|
self.cursor.point.col += 1;
|
2017-01-07 23:13:42 +00:00
|
|
|
} else {
|
2017-01-08 03:46:02 +00:00
|
|
|
self.input_needs_wrap = true;
|
2016-07-30 03:16:13 +00:00
|
|
|
}
|
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
2017-04-18 17:42:29 +00:00
|
|
|
#[inline]
|
|
|
|
fn dectest(&mut self) {
|
|
|
|
trace!("dectest");
|
|
|
|
let mut template = self.cursor.template;
|
|
|
|
template.c = 'E';
|
|
|
|
|
2017-10-13 03:29:35 +00:00
|
|
|
self.grid.region_mut(..)
|
|
|
|
.each(|c| c.reset(&template));
|
2017-04-18 17:42:29 +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) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("goto: line={}, col={}", line, col);
|
2017-10-12 01:52:23 +00:00
|
|
|
let (y_offset, max_y) = if self.mode.contains(mode::TermMode::ORIGIN) {
|
2017-04-20 03:08:44 +00:00
|
|
|
(self.scroll_region.start, self.scroll_region.end - 1)
|
2017-04-19 05:59:24 +00:00
|
|
|
} else {
|
|
|
|
(Line(0), self.grid.num_lines() - 1)
|
|
|
|
};
|
|
|
|
|
|
|
|
self.cursor.point.line = min(line + y_offset, max_y);
|
2017-02-01 17:55:23 +00:00
|
|
|
self.cursor.point.col = min(col, self.grid.num_cols() - 1);
|
2017-01-08 03:46:02 +00:00
|
|
|
self.input_needs_wrap = false;
|
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) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("goto_line: {}", line);
|
2017-04-19 05:59:24 +00:00
|
|
|
let col = self.cursor.point.col; // borrowck
|
|
|
|
self.goto(line, col)
|
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) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("goto_col: {}", col);
|
2017-04-19 05:59:24 +00:00
|
|
|
let line = self.cursor.point.line; // borrowck
|
|
|
|
self.goto(line, 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
|
2017-01-12 03:00:29 +00:00
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
let count = min(count, self.size_info.cols() - self.cursor.point.col);
|
2016-10-10 15:29:51 +00:00
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
let source = self.cursor.point.col;
|
|
|
|
let destination = self.cursor.point.col + count;
|
2016-10-10 15:29:51 +00:00
|
|
|
let num_cells = (self.size_info.cols() - destination).0;
|
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
let line = self.cursor.point.line; // borrowck
|
2016-10-10 15:29:51 +00:00
|
|
|
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.
|
2017-02-27 08:12:04 +00:00
|
|
|
let template = self.cursor.template;
|
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) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("move_up: {}", lines);
|
2017-04-19 05:59:24 +00:00
|
|
|
let move_to = Line(self.cursor.point.line.0.saturating_sub(lines.0));
|
|
|
|
let col = self.cursor.point.col; // borrowck
|
|
|
|
self.goto(move_to, col)
|
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) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("move_down: {}", lines);
|
2017-04-19 05:59:24 +00:00
|
|
|
let move_to = self.cursor.point.line + lines;
|
|
|
|
let col = self.cursor.point.col; // borrowck
|
|
|
|
self.goto(move_to, col)
|
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) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("move_forward: {}", cols);
|
2017-02-01 17:55:23 +00:00
|
|
|
self.cursor.point.col = min(self.cursor.point.col + cols, self.grid.num_cols() - 1);
|
2017-01-08 03:46:02 +00:00
|
|
|
self.input_needs_wrap = false;
|
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) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("move_backward: {}", cols);
|
2017-02-01 17:55:23 +00:00
|
|
|
self.cursor.point.col -= min(self.cursor.point.col, cols);
|
2017-01-08 03:46:02 +00:00
|
|
|
self.input_needs_wrap = false;
|
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) {
|
2016-12-17 06:13:51 +00:00
|
|
|
let _ = writer.write_all(b"\x1b[?6c");
|
2016-07-04 20:37:08 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
2017-05-07 20:08:23 +00:00
|
|
|
#[inline]
|
2017-05-08 14:50:34 +00:00
|
|
|
fn device_status<W: io::Write>(&mut self, writer: &mut W, arg: usize) {
|
|
|
|
trace!("device status: {}", arg);
|
|
|
|
match arg {
|
|
|
|
5 => {
|
|
|
|
let _ = writer.write_all(b"\x1b[0n");
|
|
|
|
},
|
|
|
|
6 => {
|
|
|
|
let pos = self.cursor.point;
|
|
|
|
let _ = write!(writer, "\x1b[{};{}R", pos.line + 1, pos.col + 1);
|
|
|
|
},
|
|
|
|
_ => debug!("unknown device status query: {}", arg),
|
|
|
|
};
|
2017-05-07 20:08:23 +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) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("[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) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("[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) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("put_tab: {}", count);
|
2016-05-31 03:44:37 +00:00
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
let mut col = self.cursor.point.col;
|
2016-07-04 00:00:00 +00:00
|
|
|
while col < self.grid.num_cols() && count != 0 {
|
2016-05-31 03:44:37 +00:00
|
|
|
count -= 1;
|
|
|
|
loop {
|
2017-04-04 12:08:10 +00:00
|
|
|
if (col + 1) == self.grid.num_cols() {
|
2016-05-31 03:44:37 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-02-27 08:12:04 +00:00
|
|
|
|
2016-07-04 00:00:00 +00:00
|
|
|
col += 1;
|
2017-04-04 12:08:10 +00:00
|
|
|
|
|
|
|
if self.tabs[*col as usize] {
|
|
|
|
break;
|
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
self.cursor.point.col = col;
|
2017-01-08 03:46:02 +00:00
|
|
|
self.input_needs_wrap = false;
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Backspace `count` characters
|
|
|
|
#[inline]
|
2016-06-09 15:37:59 +00:00
|
|
|
fn backspace(&mut self) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("backspace");
|
2017-02-01 17:55:23 +00:00
|
|
|
if self.cursor.point.col > Column(0) {
|
|
|
|
self.cursor.point.col -= 1;
|
2017-01-08 03:46:02 +00:00
|
|
|
self.input_needs_wrap = false;
|
2016-12-10 18:14:45 +00:00
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Carriage return
|
|
|
|
#[inline]
|
|
|
|
fn carriage_return(&mut self) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("carriage_return");
|
2017-02-01 17:55:23 +00:00
|
|
|
self.cursor.point.col = Column(0);
|
2017-01-08 03:46:02 +00:00
|
|
|
self.input_needs_wrap = false;
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Linefeed
|
|
|
|
#[inline]
|
|
|
|
fn linefeed(&mut self) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("linefeed");
|
2017-11-11 16:45:17 +00:00
|
|
|
let next = self.cursor.point.line + 1;
|
|
|
|
if next == self.scroll_region.end {
|
2016-08-20 03:35:13 +00:00
|
|
|
self.scroll_up(Line(1));
|
2017-11-11 16:45:17 +00:00
|
|
|
} else if next < self.grid.num_lines() {
|
|
|
|
self.cursor.point.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) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("bell");
|
2017-02-03 23:34:52 +00:00
|
|
|
self.visual_bell.ring();
|
2017-10-21 23:03:58 +00:00
|
|
|
self.next_is_urgent = Some(true);
|
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 substitute(&mut self) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("[unimplemented] substitute");
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
2017-04-18 17:41:11 +00:00
|
|
|
/// Run LF/NL
|
|
|
|
///
|
|
|
|
/// LF/NL mode has some interesting history. According to ECMA-48 4th
|
|
|
|
/// edition, in LINE FEED mode,
|
|
|
|
///
|
2017-10-30 15:03:58 +00:00
|
|
|
/// > The execution of the formatter functions LINE FEED (LF), FORM FEED
|
2017-04-18 17:41:11 +00:00
|
|
|
/// (FF), LINE TABULATION (VT) cause only movement of the active position in
|
|
|
|
/// the direction of the line progression.
|
|
|
|
///
|
|
|
|
/// In NEW LINE mode,
|
|
|
|
///
|
2017-10-30 15:03:58 +00:00
|
|
|
/// > The execution of the formatter functions LINE FEED (LF), FORM FEED
|
2017-04-18 17:41:11 +00:00
|
|
|
/// (FF), LINE TABULATION (VT) cause movement to the line home position on
|
|
|
|
/// the following line, the following form, etc. In the case of LF this is
|
|
|
|
/// referred to as the New Line (NL) option.
|
|
|
|
///
|
|
|
|
/// Additionally, ECMA-48 4th edition says that this option is deprecated.
|
|
|
|
/// ECMA-48 5th edition only mentions this option (without explanation)
|
|
|
|
/// saying that it's been removed.
|
|
|
|
///
|
|
|
|
/// As an emulator, we need to support it since applications may still rely
|
|
|
|
/// on it.
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn newline(&mut self) {
|
2017-04-18 17:41:11 +00:00
|
|
|
self.linefeed();
|
|
|
|
|
2017-10-12 01:52:23 +00:00
|
|
|
if self.mode.contains(mode::TermMode::LINE_FEED_NEW_LINE) {
|
2017-04-18 17:41:11 +00:00
|
|
|
self.carriage_return();
|
|
|
|
}
|
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) {
|
2017-05-01 04:11:47 +00:00
|
|
|
trace!("set_horizontal_tabstop");
|
|
|
|
let column = self.cursor.point.col;
|
|
|
|
self.tabs[column.0] = true;
|
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) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("insert_blank_lines: {}", lines);
|
2017-02-01 17:55:23 +00:00
|
|
|
if self.scroll_region.contains_(self.cursor.point.line) {
|
|
|
|
let origin = self.cursor.point.line;
|
2016-08-22 15:37:50 +00:00
|
|
|
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) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("delete_lines: {}", lines);
|
2017-02-01 17:55:23 +00:00
|
|
|
if self.scroll_region.contains_(self.cursor.point.line) {
|
|
|
|
let origin = self.cursor.point.line;
|
2016-08-22 15:37:50 +00:00
|
|
|
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) {
|
2017-02-01 17:55:23 +00:00
|
|
|
trace!("erase_chars: {}, {}", count, self.cursor.point.col);
|
|
|
|
let start = self.cursor.point.col;
|
2017-01-08 03:46:02 +00:00
|
|
|
let end = min(start + count, self.grid.num_cols() - 1);
|
2016-07-02 04:13:09 +00:00
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
let row = &mut self.grid[self.cursor.point.line];
|
2017-06-14 16:38:44 +00:00
|
|
|
let template = self.cursor.template; // Cleared cells have current background color set
|
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
|
2017-01-08 03:46:02 +00:00
|
|
|
let count = min(count, self.size_info.cols());
|
2017-01-12 03:00:29 +00:00
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
let start = self.cursor.point.col;
|
2017-01-08 03:46:02 +00:00
|
|
|
let end = min(start + count, self.grid.num_cols() - 1);
|
2016-09-15 15:49:55 +00:00
|
|
|
let n = (self.size_info.cols() - end).0;
|
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
let line = self.cursor.point.line; // borrowck
|
2016-09-15 15:49:55 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-12-17 06:48:04 +00:00
|
|
|
// Clear last `count` cells in line. If deleting 1 char, need to delete
|
|
|
|
// 1 cell.
|
2017-08-09 18:22:07 +00:00
|
|
|
let template = self.cursor.template;
|
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) {
|
2017-06-15 19:46:53 +00:00
|
|
|
trace!("move_backward_tabs: {}", count);
|
|
|
|
|
|
|
|
for _ in 0..count {
|
|
|
|
let mut col = self.cursor.point.col;
|
|
|
|
for i in (0..(col.0)).rev() {
|
|
|
|
if self.tabs[i as usize] {
|
|
|
|
col = index::Column(i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.cursor.point.col = col;
|
|
|
|
}
|
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) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("[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) {
|
2017-02-01 17:55:23 +00:00
|
|
|
trace!("CursorSave");
|
2017-09-28 00:29:44 +00:00
|
|
|
let cursor = if self.alt {
|
2017-02-01 17:55:23 +00:00
|
|
|
&mut self.cursor_save_alt
|
|
|
|
} else {
|
|
|
|
&mut self.cursor_save
|
|
|
|
};
|
|
|
|
|
2017-08-09 18:24:42 +00:00
|
|
|
*cursor = self.cursor;
|
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) {
|
2017-02-01 17:55:23 +00:00
|
|
|
trace!("CursorRestore");
|
2017-08-09 18:24:42 +00:00
|
|
|
let source = if self.alt {
|
2017-02-01 17:55:23 +00:00
|
|
|
&self.cursor_save_alt
|
|
|
|
} else {
|
|
|
|
&self.cursor_save
|
|
|
|
};
|
|
|
|
|
2017-08-09 18:24:42 +00:00
|
|
|
self.cursor = *source;
|
2017-02-04 03:42:25 +00:00
|
|
|
self.cursor.point.line = min(self.cursor.point.line, self.grid.num_lines() - 1);
|
|
|
|
self.cursor.point.col = min(self.cursor.point.col, self.grid.num_cols() - 1);
|
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) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("clear_line: {:?}", mode);
|
2017-04-20 03:43:02 +00:00
|
|
|
let mut template = self.cursor.template;
|
|
|
|
template.flags ^= template.flags;
|
|
|
|
|
2017-02-01 17:55:23 +00:00
|
|
|
let col = self.cursor.point.col;
|
2017-01-12 03:00:29 +00:00
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
match mode {
|
|
|
|
ansi::LineClearMode::Right => {
|
2017-02-01 17:55:23 +00:00
|
|
|
let row = &mut self.grid[self.cursor.point.line];
|
2017-01-07 05:08:52 +00:00
|
|
|
for cell in &mut row[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 => {
|
2017-02-01 17:55:23 +00:00
|
|
|
let row = &mut self.grid[self.cursor.point.line];
|
2017-01-07 05:08:52 +00:00
|
|
|
for cell in &mut row[..(col + 1)] {
|
2016-08-22 15:11:34 +00:00
|
|
|
cell.reset(&template);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
ansi::LineClearMode::All => {
|
2017-02-01 17:55:23 +00:00
|
|
|
let row = &mut self.grid[self.cursor.point.line];
|
2016-08-22 15:11:34 +00:00
|
|
|
for cell in &mut row[..] {
|
|
|
|
cell.reset(&template);
|
|
|
|
}
|
|
|
|
},
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
2017-02-06 21:28:33 +00:00
|
|
|
/// Set the indexed color value
|
|
|
|
#[inline]
|
|
|
|
fn set_color(&mut self, index: usize, color: Rgb) {
|
|
|
|
trace!("set_color[{}] = {:?}", index, color);
|
|
|
|
self.colors[index] = color;
|
2017-08-29 15:54:12 +00:00
|
|
|
self.color_modified[index] = true;
|
2017-02-06 21:28:33 +00:00
|
|
|
}
|
|
|
|
|
2017-07-28 22:14:18 +00:00
|
|
|
/// Reset the indexed color to original value
|
|
|
|
#[inline]
|
|
|
|
fn reset_color(&mut self, index: usize) {
|
|
|
|
trace!("reset_color[{}]", index);
|
|
|
|
self.colors[index] = self.original_colors[index];
|
2017-08-29 15:54:12 +00:00
|
|
|
self.color_modified[index] = false;
|
2017-07-28 22:14:18 +00:00
|
|
|
}
|
|
|
|
|
2018-01-02 16:32:50 +00:00
|
|
|
/// Set the clipboard
|
|
|
|
#[inline]
|
|
|
|
fn set_clipboard(&mut self, string: &str)
|
|
|
|
{
|
|
|
|
Clipboard::new()
|
|
|
|
.and_then(|mut clipboard| clipboard.store_primary(string))
|
|
|
|
.unwrap_or_else(|err| {
|
|
|
|
warn!("Error storing selection to clipboard. {}", err);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
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) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("clear_screen: {:?}", mode);
|
2017-04-20 03:43:02 +00:00
|
|
|
let mut template = self.cursor.template;
|
|
|
|
template.flags ^= template.flags;
|
|
|
|
|
2016-05-31 03:44:37 +00:00
|
|
|
match mode {
|
|
|
|
ansi::ClearMode::Below => {
|
2017-02-06 20:11:35 +00:00
|
|
|
for cell in &mut self.grid[self.cursor.point.line][self.cursor.point.col..] {
|
|
|
|
cell.reset(&template);
|
|
|
|
}
|
|
|
|
if self.cursor.point.line < self.grid.num_lines() - 1 {
|
2017-10-13 03:12:29 +00:00
|
|
|
self.grid.region_mut((self.cursor.point.line + 1)..)
|
|
|
|
.each(|cell| cell.reset(&template));
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
ansi::ClearMode::All => {
|
2017-10-13 03:12:29 +00:00
|
|
|
self.grid.region_mut(..).each(|c| c.reset(&template));
|
2016-05-31 03:44:37 +00:00
|
|
|
},
|
2017-02-06 21:13:25 +00:00
|
|
|
ansi::ClearMode::Above => {
|
|
|
|
// If clearing more than one line
|
|
|
|
if self.cursor.point.line > Line(1) {
|
|
|
|
// Fully clear all lines before the current line
|
2017-10-13 03:12:29 +00:00
|
|
|
self.grid.region_mut(..self.cursor.point.line)
|
|
|
|
.each(|cell| cell.reset(&template));
|
2017-02-06 21:13:25 +00:00
|
|
|
}
|
|
|
|
// Clear up to the current column in the current line
|
2017-04-18 17:42:29 +00:00
|
|
|
let end = min(self.cursor.point.col + 1, self.grid.num_cols());
|
|
|
|
for cell in &mut self.grid[self.cursor.point.line][..end] {
|
2017-02-06 21:13:25 +00:00
|
|
|
cell.reset(&template);
|
|
|
|
}
|
2017-03-04 04:22:54 +00:00
|
|
|
},
|
|
|
|
// If scrollback is implemented, this should clear it
|
|
|
|
ansi::ClearMode::Saved => return
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
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) {
|
2017-05-01 04:11:47 +00:00
|
|
|
trace!("clear_tabs: {:?}", mode);
|
|
|
|
match mode {
|
|
|
|
ansi::TabulationClearMode::Current => {
|
|
|
|
let column = self.cursor.point.col;
|
|
|
|
self.tabs[column.0] = false;
|
|
|
|
},
|
|
|
|
ansi::TabulationClearMode::All => {
|
|
|
|
let len = self.tabs.len();
|
|
|
|
// Safe since false boolean is null, each item occupies only 1
|
|
|
|
// byte, and called on the length of the vec.
|
|
|
|
unsafe {
|
|
|
|
::std::ptr::write_bytes(self.tabs.as_mut_ptr(), 0, len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-07-04 15:42:18 +00:00
|
|
|
}
|
2016-07-04 00:07:23 +00:00
|
|
|
|
2018-01-18 17:27:07 +00:00
|
|
|
// Reset all important fields in the term struct
|
2016-07-04 00:07:23 +00:00
|
|
|
#[inline]
|
2016-07-04 15:42:18 +00:00
|
|
|
fn reset_state(&mut self) {
|
2018-01-18 17:27:07 +00:00
|
|
|
self.input_needs_wrap = false;
|
|
|
|
self.next_title = None;
|
|
|
|
self.next_mouse_cursor = None;
|
|
|
|
self.alt = false;
|
|
|
|
self.cursor = Default::default();
|
|
|
|
self.active_charset = Default::default();
|
|
|
|
self.mode = Default::default();
|
|
|
|
self.font_size = self.original_font_size;
|
|
|
|
self.next_is_urgent = None;
|
|
|
|
self.cursor_save = Default::default();
|
|
|
|
self.cursor_save_alt = Default::default();
|
|
|
|
self.colors = self.original_colors;
|
|
|
|
self.color_modified = [false; color::COUNT];
|
|
|
|
self.cursor_style = None;
|
2018-04-28 14:14:45 +00:00
|
|
|
self.grid.reset();
|
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) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("reverse_index");
|
2016-05-31 03:44:37 +00:00
|
|
|
// if cursor is at the top
|
2017-02-01 17:55:23 +00:00
|
|
|
if self.cursor.point.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 {
|
2017-02-01 17:55:23 +00:00
|
|
|
self.cursor.point.line -= min(self.cursor.point.line, 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) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("Set Attribute: {:?}", attr);
|
2016-05-31 03:44:37 +00:00
|
|
|
match attr {
|
2017-02-01 17:55:23 +00:00
|
|
|
Attr::Foreground(color) => self.cursor.template.fg = color,
|
|
|
|
Attr::Background(color) => self.cursor.template.bg = color,
|
2016-05-31 03:44:37 +00:00
|
|
|
Attr::Reset => {
|
2017-02-01 17:55:23 +00:00
|
|
|
self.cursor.template.fg = Color::Named(NamedColor::Foreground);
|
|
|
|
self.cursor.template.bg = Color::Named(NamedColor::Background);
|
|
|
|
self.cursor.template.flags = cell::Flags::empty();
|
2016-06-06 23:54:15 +00:00
|
|
|
},
|
2017-10-12 01:52:23 +00:00
|
|
|
Attr::Reverse => self.cursor.template.flags.insert(cell::Flags::INVERSE),
|
|
|
|
Attr::CancelReverse => self.cursor.template.flags.remove(cell::Flags::INVERSE),
|
|
|
|
Attr::Bold => self.cursor.template.flags.insert(cell::Flags::BOLD),
|
|
|
|
Attr::CancelBold => self.cursor.template.flags.remove(cell::Flags::BOLD),
|
|
|
|
Attr::Dim => self.cursor.template.flags.insert(cell::Flags::DIM),
|
|
|
|
Attr::CancelBoldDim => self.cursor.template.flags.remove(cell::Flags::BOLD | cell::Flags::DIM),
|
|
|
|
Attr::Italic => self.cursor.template.flags.insert(cell::Flags::ITALIC),
|
|
|
|
Attr::CancelItalic => self.cursor.template.flags.remove(cell::Flags::ITALIC),
|
|
|
|
Attr::Underscore => self.cursor.template.flags.insert(cell::Flags::UNDERLINE),
|
|
|
|
Attr::CancelUnderline => self.cursor.template.flags.remove(cell::Flags::UNDERLINE),
|
2016-05-31 03:44:37 +00:00
|
|
|
_ => {
|
2017-01-13 07:15:06 +00:00
|
|
|
debug!("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) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("set_mode: {:?}", mode);
|
2016-05-31 03:44:37 +00:00
|
|
|
match mode {
|
2017-02-01 17:55:23 +00:00
|
|
|
ansi::Mode::SwapScreenAndSetRestoreCursor => {
|
2017-10-12 01:52:23 +00:00
|
|
|
self.mode.insert(mode::TermMode::ALT_SCREEN);
|
2017-02-01 17:55:23 +00:00
|
|
|
self.save_cursor_position();
|
2017-08-09 18:24:51 +00:00
|
|
|
if !self.alt {
|
|
|
|
self.swap_alt();
|
|
|
|
}
|
2017-02-03 04:51:14 +00:00
|
|
|
self.save_cursor_position();
|
2017-02-01 17:55:23 +00:00
|
|
|
},
|
2017-10-12 01:52:23 +00:00
|
|
|
ansi::Mode::ShowCursor => self.mode.insert(mode::TermMode::SHOW_CURSOR),
|
|
|
|
ansi::Mode::CursorKeys => self.mode.insert(mode::TermMode::APP_CURSOR),
|
2017-12-24 20:15:42 +00:00
|
|
|
ansi::Mode::ReportMouseClicks => {
|
|
|
|
self.mode.insert(mode::TermMode::MOUSE_REPORT_CLICK);
|
|
|
|
self.set_mouse_cursor(MouseCursor::Arrow);
|
|
|
|
},
|
2018-01-26 21:28:43 +00:00
|
|
|
ansi::Mode::ReportCellMouseMotion => {
|
|
|
|
self.mode.insert(mode::TermMode::MOUSE_DRAG);
|
|
|
|
self.set_mouse_cursor(MouseCursor::Arrow);
|
|
|
|
},
|
2017-12-24 20:15:42 +00:00
|
|
|
ansi::Mode::ReportAllMouseMotion => {
|
|
|
|
self.mode.insert(mode::TermMode::MOUSE_MOTION);
|
|
|
|
self.set_mouse_cursor(MouseCursor::Arrow);
|
|
|
|
},
|
2017-10-12 01:52:23 +00:00
|
|
|
ansi::Mode::ReportFocusInOut => self.mode.insert(mode::TermMode::FOCUS_IN_OUT),
|
|
|
|
ansi::Mode::BracketedPaste => self.mode.insert(mode::TermMode::BRACKETED_PASTE),
|
|
|
|
ansi::Mode::SgrMouse => self.mode.insert(mode::TermMode::SGR_MOUSE),
|
|
|
|
ansi::Mode::LineWrap => self.mode.insert(mode::TermMode::LINE_WRAP),
|
|
|
|
ansi::Mode::LineFeedNewLine => self.mode.insert(mode::TermMode::LINE_FEED_NEW_LINE),
|
|
|
|
ansi::Mode::Origin => self.mode.insert(mode::TermMode::ORIGIN),
|
2017-04-19 16:03:33 +00:00
|
|
|
ansi::Mode::DECCOLM => self.deccolm(),
|
2017-10-12 01:52:23 +00:00
|
|
|
ansi::Mode::Insert => self.mode.insert(mode::TermMode::INSERT), // heh
|
2016-06-08 04:17:48 +00:00
|
|
|
_ => {
|
2017-05-06 15:45:23 +00:00
|
|
|
trace!(".. 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) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("unset_mode: {:?}", mode);
|
2016-05-31 03:44:37 +00:00
|
|
|
match mode {
|
2017-02-01 17:55:23 +00:00
|
|
|
ansi::Mode::SwapScreenAndSetRestoreCursor => {
|
2017-10-12 01:52:23 +00:00
|
|
|
self.mode.remove(mode::TermMode::ALT_SCREEN);
|
2017-02-01 17:55:23 +00:00
|
|
|
self.restore_cursor_position();
|
2017-08-09 18:24:51 +00:00
|
|
|
if self.alt {
|
|
|
|
self.swap_alt();
|
|
|
|
}
|
2017-02-03 04:51:14 +00:00
|
|
|
self.restore_cursor_position();
|
2017-02-01 17:55:23 +00:00
|
|
|
},
|
2017-10-12 01:52:23 +00:00
|
|
|
ansi::Mode::ShowCursor => self.mode.remove(mode::TermMode::SHOW_CURSOR),
|
|
|
|
ansi::Mode::CursorKeys => self.mode.remove(mode::TermMode::APP_CURSOR),
|
2017-12-24 20:15:42 +00:00
|
|
|
ansi::Mode::ReportMouseClicks => {
|
|
|
|
self.mode.remove(mode::TermMode::MOUSE_REPORT_CLICK);
|
|
|
|
self.set_mouse_cursor(MouseCursor::Text);
|
|
|
|
},
|
2018-01-26 21:28:43 +00:00
|
|
|
ansi::Mode::ReportCellMouseMotion => {
|
|
|
|
self.mode.remove(mode::TermMode::MOUSE_DRAG);
|
|
|
|
self.set_mouse_cursor(MouseCursor::Text);
|
|
|
|
},
|
2017-12-24 20:15:42 +00:00
|
|
|
ansi::Mode::ReportAllMouseMotion => {
|
|
|
|
self.mode.remove(mode::TermMode::MOUSE_MOTION);
|
|
|
|
self.set_mouse_cursor(MouseCursor::Text);
|
|
|
|
},
|
2017-10-12 01:52:23 +00:00
|
|
|
ansi::Mode::ReportFocusInOut => self.mode.remove(mode::TermMode::FOCUS_IN_OUT),
|
|
|
|
ansi::Mode::BracketedPaste => self.mode.remove(mode::TermMode::BRACKETED_PASTE),
|
|
|
|
ansi::Mode::SgrMouse => self.mode.remove(mode::TermMode::SGR_MOUSE),
|
|
|
|
ansi::Mode::LineWrap => self.mode.remove(mode::TermMode::LINE_WRAP),
|
|
|
|
ansi::Mode::LineFeedNewLine => self.mode.remove(mode::TermMode::LINE_FEED_NEW_LINE),
|
|
|
|
ansi::Mode::Origin => self.mode.remove(mode::TermMode::ORIGIN),
|
2017-04-19 16:03:33 +00:00
|
|
|
ansi::Mode::DECCOLM => self.deccolm(),
|
2017-10-12 01:52:23 +00:00
|
|
|
ansi::Mode::Insert => self.mode.remove(mode::TermMode::INSERT),
|
2016-06-08 04:17:48 +00:00
|
|
|
_ => {
|
2017-05-06 15:45:23 +00:00
|
|
|
trace!(".. 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>) {
|
2017-01-13 07:15:06 +00:00
|
|
|
trace!("set scroll region: {:?}", region);
|
2017-01-10 06:05:26 +00:00
|
|
|
self.scroll_region.start = min(region.start, self.grid.num_lines());
|
|
|
|
self.scroll_region.end = min(region.end, self.grid.num_lines());
|
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) {
|
2017-10-12 01:52:23 +00:00
|
|
|
trace!("set mode::TermMode::APP_KEYPAD");
|
|
|
|
self.mode.insert(mode::TermMode::APP_KEYPAD);
|
2016-07-04 16:19:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn unset_keypad_application_mode(&mut self) {
|
2017-10-12 01:52:23 +00:00
|
|
|
trace!("unset mode::TermMode::APP_KEYPAD");
|
|
|
|
self.mode.remove(mode::TermMode::APP_KEYPAD);
|
2016-07-04 16:19:48 +00:00
|
|
|
}
|
2017-01-09 20:07:23 +00:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn configure_charset(&mut self, index: CharsetIndex, charset: StandardCharset) {
|
2017-01-23 16:50:24 +00:00
|
|
|
trace!("designate {:?} character set as {:?}", index, charset);
|
2017-02-01 17:55:23 +00:00
|
|
|
self.cursor.charsets[index] = charset;
|
2017-01-09 20:07:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn set_active_charset(&mut self, index: CharsetIndex) {
|
2017-01-23 16:50:24 +00:00
|
|
|
trace!("Activate {:?} character set", index);
|
2017-01-09 20:07:23 +00:00
|
|
|
self.active_charset = index;
|
|
|
|
}
|
2017-05-28 16:38:10 +00:00
|
|
|
|
|
|
|
#[inline]
|
2017-12-03 21:38:42 +00:00
|
|
|
fn set_cursor_style(&mut self, style: Option<CursorStyle>) {
|
2017-05-28 16:38:10 +00:00
|
|
|
trace!("set_cursor_style {:?}", style);
|
|
|
|
self.cursor_style = style;
|
|
|
|
}
|
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
|
|
|
|
2017-04-20 17:18:05 +00:00
|
|
|
use super::{Cell, Term, SizeInfo};
|
2017-01-15 01:53:48 +00:00
|
|
|
use term::cell;
|
2016-11-20 00:16:20 +00:00
|
|
|
|
|
|
|
use grid::Grid;
|
2017-01-09 20:07:23 +00:00
|
|
|
use index::{Point, Line, Column};
|
|
|
|
use ansi::{Handler, CharsetIndex, StandardCharset};
|
2017-01-15 01:53:48 +00:00
|
|
|
use selection::Selection;
|
|
|
|
use std::mem;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn semantic_selection_works() {
|
|
|
|
let size = SizeInfo {
|
|
|
|
width: 21.0,
|
|
|
|
height: 51.0,
|
|
|
|
cell_width: 3.0,
|
|
|
|
cell_height: 3.0,
|
2017-05-06 15:45:23 +00:00
|
|
|
padding_x: 0.0,
|
|
|
|
padding_y: 0.0,
|
2017-01-15 01:53:48 +00:00
|
|
|
};
|
|
|
|
let mut term = Term::new(&Default::default(), size);
|
2018-03-09 21:49:47 +00:00
|
|
|
let mut grid: Grid<Cell> = Grid::new(Line(3), Column(5), 0, Cell::default());
|
2017-01-15 01:53:48 +00:00
|
|
|
for i in 0..5 {
|
|
|
|
for j in 0..2 {
|
|
|
|
grid[Line(j)][Column(i)].c = 'a';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
grid[Line(0)][Column(0)].c = '"';
|
|
|
|
grid[Line(0)][Column(3)].c = '"';
|
|
|
|
grid[Line(1)][Column(2)].c = '"';
|
2017-10-12 01:52:23 +00:00
|
|
|
grid[Line(0)][Column(4)].flags.insert(cell::Flags::WRAPLINE);
|
2017-01-15 01:53:48 +00:00
|
|
|
|
|
|
|
let mut escape_chars = String::from("\"");
|
|
|
|
|
|
|
|
mem::swap(&mut term.grid, &mut grid);
|
|
|
|
mem::swap(&mut term.semantic_escape_chars, &mut escape_chars);
|
|
|
|
|
|
|
|
{
|
2018-05-30 08:20:47 +00:00
|
|
|
*term.selection_mut() = Some(Selection::semantic(Point { line: 2, col: Column(1) }));
|
2018-03-09 21:49:47 +00:00
|
|
|
assert_eq!(term.selection_to_string(), Some(String::from("aa")));
|
2017-01-15 01:53:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2018-05-30 08:20:47 +00:00
|
|
|
*term.selection_mut() = Some(Selection::semantic(Point { line: 2, col: Column(4) }));
|
2018-03-09 21:49:47 +00:00
|
|
|
assert_eq!(term.selection_to_string(), Some(String::from("aaa")));
|
2017-01-15 01:53:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2018-05-30 08:20:47 +00:00
|
|
|
*term.selection_mut() = Some(Selection::semantic(Point { line: 1, col: Column(1) }));
|
2018-03-09 21:49:47 +00:00
|
|
|
assert_eq!(term.selection_to_string(), Some(String::from("aaa")));
|
2017-01-15 01:53:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn line_selection_works() {
|
|
|
|
let size = SizeInfo {
|
|
|
|
width: 21.0,
|
|
|
|
height: 51.0,
|
|
|
|
cell_width: 3.0,
|
|
|
|
cell_height: 3.0,
|
2017-05-06 15:45:23 +00:00
|
|
|
padding_x: 0.0,
|
|
|
|
padding_y: 0.0,
|
2017-01-15 01:53:48 +00:00
|
|
|
};
|
|
|
|
let mut term = Term::new(&Default::default(), size);
|
2018-03-09 21:49:47 +00:00
|
|
|
let mut grid: Grid<Cell> = Grid::new(Line(1), Column(5), 0, Cell::default());
|
2017-01-15 01:53:48 +00:00
|
|
|
for i in 0..5 {
|
|
|
|
grid[Line(0)][Column(i)].c = 'a';
|
|
|
|
}
|
|
|
|
grid[Line(0)][Column(0)].c = '"';
|
|
|
|
grid[Line(0)][Column(3)].c = '"';
|
|
|
|
|
|
|
|
|
|
|
|
mem::swap(&mut term.grid, &mut grid);
|
|
|
|
|
2018-03-09 21:49:47 +00:00
|
|
|
*term.selection_mut() = Some(Selection::lines(Point { line: 0, col: Column(3) }));
|
|
|
|
assert_eq!(term.selection_to_string(), Some(String::from("\"aa\"a\n")));
|
2017-01-15 01:53:48 +00:00
|
|
|
}
|
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() {
|
2016-12-30 02:43:55 +00:00
|
|
|
let template = Cell::default();
|
2016-11-20 00:16:20 +00:00
|
|
|
|
2018-03-09 21:49:47 +00:00
|
|
|
let grid: Grid<Cell> = Grid::new(Line(24), Column(80), 0, template);
|
2016-11-20 00:16:20 +00:00
|
|
|
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
|
|
|
|
2017-01-09 20:07:23 +00:00
|
|
|
#[test]
|
|
|
|
fn input_line_drawing_character() {
|
|
|
|
let size = SizeInfo {
|
|
|
|
width: 21.0,
|
|
|
|
height: 51.0,
|
|
|
|
cell_width: 3.0,
|
|
|
|
cell_height: 3.0,
|
2017-05-06 15:45:23 +00:00
|
|
|
padding_x: 0.0,
|
|
|
|
padding_y: 0.0,
|
2017-01-09 20:07:23 +00:00
|
|
|
};
|
2017-01-09 03:18:39 +00:00
|
|
|
let mut term = Term::new(&Default::default(), size);
|
2017-01-09 20:07:23 +00:00
|
|
|
let cursor = Point::new(Line(0), Column(0));
|
|
|
|
term.configure_charset(CharsetIndex::G0,
|
|
|
|
StandardCharset::SpecialCharacterAndLineDrawing);
|
|
|
|
term.input('a');
|
|
|
|
|
|
|
|
assert_eq!(term.grid()[&cursor].c, '▒');
|
|
|
|
}
|
2016-11-20 00:16:20 +00:00
|
|
|
}
|
2016-11-28 22:13:11 +00:00
|
|
|
|
2017-02-03 04:50:48 +00:00
|
|
|
#[cfg(all(test, feature = "bench"))]
|
2016-12-30 02:51:17 +00:00
|
|
|
mod benches {
|
2016-11-28 22:13:11 +00:00
|
|
|
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;
|
2017-05-28 16:38:10 +00:00
|
|
|
use config::Config;
|
2016-11-28 22:13:11 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
///
|
2016-12-17 06:48:04 +00:00
|
|
|
/// 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.
|
2016-11-28 22:13:11 +00:00
|
|
|
///
|
2016-12-17 06:48:04 +00:00
|
|
|
/// 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).
|
2016-11-28 22:13:11 +00:00
|
|
|
#[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();
|
|
|
|
|
2017-05-28 16:38:10 +00:00
|
|
|
let config = Config::default();
|
|
|
|
|
|
|
|
let mut terminal = Term::new(&config, size);
|
2016-11-28 22:13:11 +00:00
|
|
|
mem::swap(&mut terminal.grid, &mut grid);
|
|
|
|
|
|
|
|
b.iter(|| {
|
2018-03-09 13:42:11 +00:00
|
|
|
let iter = terminal.renderable_cells(&config, None, false);
|
2016-11-28 22:13:11 +00:00
|
|
|
for cell in iter {
|
|
|
|
test::black_box(cell);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|