diff options
Diffstat (limited to 'src/term/mod.rs')
-rw-r--r-- | src/term/mod.rs | 339 |
1 files changed, 131 insertions, 208 deletions
diff --git a/src/term/mod.rs b/src/term/mod.rs index 48eedef1..9e879ada 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -18,7 +18,8 @@ use std::ops::{Index, IndexMut, Range, RangeInclusive}; use std::time::{Duration, Instant}; use std::{io, mem, ptr}; -use arraydeque::ArrayDeque; +use copypasta::{Clipboard, Load, Store}; +use font::{self, RasterizedGlyph, Size}; use glutin::MouseCursor; use unicode_width::UnicodeWidthChar; @@ -26,6 +27,7 @@ use crate::ansi::{ self, Attr, CharsetIndex, Color, CursorStyle, Handler, NamedColor, StandardCharset, }; use crate::config::{Config, VisualBellAnimation}; +use crate::cursor; use crate::grid::{ BidirectionalIterator, DisplayIter, Grid, GridCell, IndexRegion, Indexed, Scroll, ViewportPosition, @@ -37,8 +39,6 @@ use crate::selection::{self, Locations, Selection}; use crate::term::cell::{Cell, Flags, LineLength}; use crate::term::color::Rgb; use crate::url::{Url, UrlParser}; -use copypasta::{Clipboard, Load, Store}; -use font::{self, Size}; #[cfg(windows)] use crate::tty; @@ -158,12 +158,12 @@ pub struct RenderableCellsIter<'a> { grid: &'a Grid<Cell>, cursor: &'a Point, cursor_offset: usize, - mode: TermMode, + cursor_cell: Option<RasterizedGlyph>, + cursor_style: CursorStyle, config: &'a Config, colors: &'a color::List, selection: Option<RangeInclusive<index::Linear>>, url_highlight: &'a Option<RangeInclusive<index::Linear>>, - cursor_cells: ArrayDeque<[Indexed<Cell>; 3]>, } impl<'a> RenderableCellsIter<'a> { @@ -176,6 +176,7 @@ impl<'a> RenderableCellsIter<'a> { config: &'b Config, selection: Option<Locations>, cursor_style: CursorStyle, + metrics: font::Metrics, ) -> RenderableCellsIter<'b> { let grid = &term.grid; @@ -224,203 +225,143 @@ impl<'a> RenderableCellsIter<'a> { } } + // Load cursor glyph + let cursor = &term.cursor.point; + let cursor_cell = + if term.mode.contains(mode::TermMode::SHOW_CURSOR) && grid.contains(cursor) { + let offset_x = config.font().offset().x; + let offset_y = config.font().offset().y; + + let is_wide = grid[cursor].flags.contains(cell::Flags::WIDE_CHAR) + && (cursor.col + 1) < grid.num_cols(); + Some(cursor::get_cursor_glyph(cursor_style, metrics, offset_x, offset_y, is_wide)) + } else { + None + }; + RenderableCellsIter { - cursor: &term.cursor.point, + cursor, cursor_offset, grid, inner, - mode: term.mode, selection: selection_range, url_highlight: &grid.url_highlight, config, colors: &term.colors, - cursor_cells: ArrayDeque::new(), + cursor_cell, + cursor_style, } - .initialize(cursor_style) - } - - fn push_cursor_cells(&mut self, original: Cell, cursor: Cell, wide: Cell) { - // 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, inner: original }) - .expect("won't exceed capacity"); - - // Prints the cursor - self.cursor_cells - .push_back(Indexed { line: self.cursor.line, column: self.cursor.col, inner: cursor }) - .expect("won't exceed capacity"); - - // If cursor is over a wide (2 cell size) character, - // print the second cursor cell - if self.is_wide_cursor(&cursor) { - self.cursor_cells - .push_back(Indexed { - line: self.cursor.line, - column: self.cursor.col + 1, - inner: wide, - }) - .expect("won't exceed capacity"); - } - } - - fn populate_block_cursor(&mut self) { - let cell = &self.grid[self.cursor]; - let text_color = self.config.cursor_text_color().unwrap_or(cell.bg); - let cursor_color = self.config.cursor_cursor_color().unwrap_or(cell.fg); - - let original_cell = self.grid[self.cursor]; - - let mut cursor_cell = self.grid[self.cursor]; - cursor_cell.fg = text_color; - cursor_cell.bg = cursor_color; - - let mut wide_cell = cursor_cell; - wide_cell.c = ' '; - - self.push_cursor_cells(original_cell, cursor_cell, wide_cell); - } - - 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.config.cursor_cursor_color().unwrap_or(cursor_cell.fg); - 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); - } - - fn populate_underline_cursor(&mut self) { - self.populate_char_cursor(font::UNDERLINE_CURSOR_CHAR, font::UNDERLINE_CURSOR_CHAR); - } - - fn populate_beam_cursor(&mut self) { - self.populate_char_cursor(font::BEAM_CURSOR_CHAR, ' '); - } - - fn populate_box_cursor(&mut self) { - self.populate_char_cursor(font::BOX_CURSOR_CHAR, ' '); } +} - #[inline] - fn is_wide_cursor(&self, cell: &Cell) -> bool { - cell.flags.contains(cell::Flags::WIDE_CHAR) && (self.cursor.col + 1) < self.grid.num_cols() - } +#[derive(Clone, Debug)] +pub enum RenderableCellContent { + Chars([char; cell::MAX_ZEROWIDTH_CHARS + 1]), + Raw(RasterizedGlyph), +} - /// 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], - }) - .expect("won't exceed capacity"); - } +#[derive(Clone, Debug)] +pub struct RenderableCell { + /// A _Display_ line (not necessarily an _Active_ line) + pub line: Line, + pub column: Column, + pub inner: RenderableCellContent, + pub fg: Rgb, + pub bg: Rgb, + pub bg_alpha: f32, + pub flags: cell::Flags, +} - fn initialize(mut self, cursor_style: CursorStyle) -> Self { - if self.cursor_is_visible() { - 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(); - }, - } +impl RenderableCell { + fn new(config: &Config, colors: &color::List, cell: Indexed<Cell>, selected: bool) -> Self { + // Lookup RGB values + let mut fg_rgb = Self::compute_fg_rgb(config, colors, cell.fg, cell.flags); + let mut bg_rgb = Self::compute_bg_rgb(colors, cell.bg); + + let selection_background = config.colors().selection.background; + let bg_alpha = if let (true, Some(col)) = (selected, selection_background) { + // Override selection background with config colors + bg_rgb = col; + 1.0 + } else if selected ^ cell.inverse() { + // Invert cell fg and bg colors + mem::swap(&mut fg_rgb, &mut bg_rgb); + Self::compute_bg_alpha(cell.fg) } else { - self.populate_no_cursor(); + Self::compute_bg_alpha(cell.bg) + }; + + // Override selection text with config colors + if let (true, Some(col)) = (selected, config.colors().selection.text) { + fg_rgb = col; } - self - } - /// Check if the cursor should be rendered. - #[inline] - fn cursor_is_visible(&self) -> bool { - self.mode.contains(mode::TermMode::SHOW_CURSOR) && self.grid.contains(self.cursor) + RenderableCell { + line: cell.line, + column: cell.column, + inner: RenderableCellContent::Chars(cell.chars()), + fg: fg_rgb, + bg: bg_rgb, + bg_alpha, + flags: cell.flags, + } } - fn compute_fg_rgb(&self, fg: Color, cell: &Cell) -> Rgb { + fn compute_fg_rgb(config: &Config, colors: &color::List, fg: Color, flags: cell::Flags) -> Rgb { match fg { Color::Spec(rgb) => rgb, Color::Named(ansi) => { - match ( - self.config.draw_bold_text_with_bright_colors(), - cell.flags & Flags::DIM_BOLD, - ) { + match (config.draw_bold_text_with_bright_colors(), flags & Flags::DIM_BOLD) { // If no bright foreground is set, treat it like the BOLD flag doesn't exist - (_, self::cell::Flags::DIM_BOLD) + (_, cell::Flags::DIM_BOLD) if ansi == NamedColor::Foreground - && self.config.colors().primary.bright_foreground.is_none() => + && config.colors().primary.bright_foreground.is_none() => { - self.colors[NamedColor::DimForeground] + colors[NamedColor::DimForeground] }, // Draw bold text in bright colors *and* contains bold flag. - (true, self::cell::Flags::BOLD) => self.colors[ansi.to_bright()], + (true, cell::Flags::BOLD) => colors[ansi.to_bright()], // Cell is marked as dim and not bold - (_, self::cell::Flags::DIM) | (false, self::cell::Flags::DIM_BOLD) => { - self.colors[ansi.to_dim()] - }, + (_, cell::Flags::DIM) | (false, cell::Flags::DIM_BOLD) => colors[ansi.to_dim()], // None of the above, keep original color. - _ => self.colors[ansi], + _ => colors[ansi], } }, Color::Indexed(idx) => { let idx = match ( - self.config.draw_bold_text_with_bright_colors(), - cell.flags & Flags::DIM_BOLD, + config.draw_bold_text_with_bright_colors(), + flags & Flags::DIM_BOLD, idx, ) { - (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, + (true, cell::Flags::BOLD, 0..=7) => idx as usize + 8, + (false, cell::Flags::DIM, 8..=15) => idx as usize - 8, + (false, cell::Flags::DIM, 0..=7) => idx as usize + 260, _ => idx as usize, }; - self.colors[idx] + colors[idx] }, } } #[inline] - fn compute_bg_alpha(&self, bg: Color) -> f32 { + fn compute_bg_alpha(bg: Color) -> f32 { match bg { Color::Named(NamedColor::Background) => 0.0, _ => 1.0, } } - fn compute_bg_rgb(&self, bg: Color) -> Rgb { + #[inline] + fn compute_bg_rgb(colors: &color::List, bg: Color) -> Rgb { match bg { Color::Spec(rgb) => rgb, - Color::Named(ansi) => self.colors[ansi], - Color::Indexed(idx) => self.colors[idx], + Color::Named(ansi) => colors[ansi], + Color::Indexed(idx) => colors[idx], } } } -#[derive(Copy, Clone, Debug)] -pub struct RenderableCell { - /// A _Display_ line (not necessarily an _Active_ line) - pub line: Line, - pub column: Column, - pub chars: [char; cell::MAX_ZEROWIDTH_CHARS + 1], - pub fg: Rgb, - pub bg: Rgb, - pub bg_alpha: f32, - pub flags: cell::Flags, -} - impl<'a> Iterator for RenderableCellsIter<'a> { type Item = RenderableCell; @@ -431,23 +372,32 @@ impl<'a> Iterator for RenderableCellsIter<'a> { #[inline] fn next(&mut self) -> Option<Self::Item> { loop { - // Handle cursor - let (cell, selected, highlighted) = if self.cursor_offset == self.inner.offset() - && self.inner.column() == self.cursor.col - { - // Cursor cell - let mut cell = self.cursor_cells.pop_front().unwrap(); - cell.line = self.inner.line(); - - // 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(); + if self.cursor_offset == self.inner.offset() && self.inner.column() == self.cursor.col { + // Handle cursor + if let Some(cursor_cell) = self.cursor_cell.take() { + let cell = Indexed { + inner: self.grid[self.cursor], + column: self.cursor.col, + line: self.cursor.line, + }; + let mut renderable_cell = + RenderableCell::new(self.config, self.colors, cell, false); + + renderable_cell.inner = RenderableCellContent::Raw(cursor_cell); + + return Some(renderable_cell); + } else { + let mut cell = + RenderableCell::new(self.config, self.colors, self.inner.next()?, false); + + if self.cursor_style == CursorStyle::Block { + std::mem::swap(&mut cell.bg, &mut cell.fg); + } + + return Some(cell); } - (cell, false, false) } else { - let cell = self.inner.next()?; + let mut cell = self.inner.next()?; let index = Linear::new(self.grid.num_cols(), cell.column, cell.line); @@ -460,51 +410,13 @@ impl<'a> Iterator for RenderableCellsIter<'a> { } // Underline URL highlights - let highlighted = self - .url_highlight - .as_ref() - .map(|range| range.contains_(index)) - .unwrap_or(false); - - (cell, selected, highlighted) - }; - - // Lookup RGB values - let mut fg_rgb = self.compute_fg_rgb(cell.fg, &cell); - let mut bg_rgb = self.compute_bg_rgb(cell.bg); - - let selection_background = self.config.colors().selection.background; - let bg_alpha = if let (true, Some(col)) = (selected, selection_background) { - // Override selection background with config colors - bg_rgb = col; - 1.0 - } else if selected ^ cell.inverse() { - // Invert cell fg and bg colors - mem::swap(&mut fg_rgb, &mut bg_rgb); - self.compute_bg_alpha(cell.fg) - } else { - self.compute_bg_alpha(cell.bg) - }; - - // Override selection text with config colors - if let (true, Some(col)) = (selected, self.config.colors().selection.text) { - fg_rgb = col; - } + if self.url_highlight.as_ref().map(|range| range.contains_(index)).unwrap_or(false) + { + cell.inner.flags.insert(Flags::UNDERLINE); + } - let mut flags = cell.flags; - if highlighted { - flags.insert(Flags::UNDERLINE); + return Some(RenderableCell::new(self.config, self.colors, cell, selected)); } - - return Some(RenderableCell { - line: cell.line, - column: cell.column, - chars: cell.chars(), - fg: fg_rgb, - bg: bg_rgb, - bg_alpha, - flags, - }); } } } @@ -1174,6 +1086,7 @@ impl Term { &'b self, config: &'b Config, window_focused: bool, + metrics: font::Metrics, ) -> RenderableCellsIter<'_> { let alt_screen = self.mode.contains(TermMode::ALT_SCREEN); let selection = self @@ -1189,7 +1102,7 @@ impl Term { CursorStyle::HollowBlock }; - RenderableCellsIter::new(&self, config, selection, cursor) + RenderableCellsIter::new(&self, config, selection, cursor, metrics) } /// Resize terminal to new dimensions @@ -2497,8 +2410,18 @@ mod benches { let mut terminal = Term::new(&config, size, MessageBuffer::new()); mem::swap(&mut terminal.grid, &mut grid); + let metrics = font::Metrics { + descent: 0., + line_height: 0., + average_advance: 0., + underline_position: 0., + underline_thickness: 0., + strikeout_position: 0., + strikeout_thickness: 0., + }; + b.iter(|| { - let iter = terminal.renderable_cells(&config, false); + let iter = terminal.renderable_cells(&config, false, metrics); for cell in iter { test::black_box(cell); } |