diff options
Diffstat (limited to 'alacritty_terminal/src/term/mod.rs')
-rw-r--r-- | alacritty_terminal/src/term/mod.rs | 279 |
1 files changed, 135 insertions, 144 deletions
diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index 00726dad..a33ac919 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -40,8 +40,13 @@ const MAX_SEARCH_LINES: usize = 100; /// Default tab interval, corresponding to terminfo `it` value. const INITIAL_TABSTOPS: usize = 8; -/// Minimum number of columns and lines. -const MIN_SIZE: usize = 2; +/// Minimum number of columns. +/// +/// A minimum of 2 is necessary to hold fullwidth unicode characters. +pub const MIN_COLS: usize = 2; + +/// Minimum number of visible lines. +pub const MIN_SCREEN_LINES: usize = 1; /// Cursor storing all information relevant for rendering. #[derive(Debug, Eq, PartialEq, Copy, Clone, Deserialize)] @@ -612,69 +617,139 @@ impl From<&BellConfig> for VisualBell { #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)] pub struct SizeInfo { /// Terminal window width. - pub width: f32, + width: f32, /// Terminal window height. - pub height: f32, + height: f32, /// Width of individual cell. - pub cell_width: f32, + cell_width: f32, /// Height of individual cell. - pub cell_height: f32, + cell_height: f32, /// Horizontal window padding. - pub padding_x: f32, + padding_x: f32, /// Horizontal window padding. - pub padding_y: f32, + padding_y: f32, - /// DPR of the current window. - #[serde(default)] - pub dpr: f64, + /// Number of lines in the viewport. + screen_lines: Line, + + /// Number of columns in the viewport. + cols: Column, } impl SizeInfo { - #[inline] - pub fn lines(&self) -> Line { - Line(((self.height - 2. * self.padding_y) / self.cell_height) as usize) - } + #[allow(clippy::too_many_arguments)] + pub fn new( + width: f32, + height: f32, + cell_width: f32, + cell_height: f32, + mut padding_x: f32, + mut padding_y: f32, + dynamic_padding: bool, + ) -> SizeInfo { + if dynamic_padding { + padding_x = Self::dynamic_padding(padding_x.floor(), width, cell_width); + padding_y = Self::dynamic_padding(padding_y.floor(), height, cell_height); + } - #[inline] - pub fn cols(&self) -> Column { - Column(((self.width - 2. * self.padding_x) / self.cell_width) as usize) - } + let lines = (height - 2. * padding_y) / cell_height; + let screen_lines = Line(max(lines as usize, MIN_SCREEN_LINES)); - #[inline] - pub fn padding_right(&self) -> usize { - (self.padding_x + (self.width - 2. * self.padding_x) % self.cell_width) as usize + let cols = (width - 2. * padding_x) / cell_width; + let cols = Column(max(cols as usize, MIN_COLS)); + + SizeInfo { + width, + height, + cell_width, + cell_height, + padding_x: padding_x.floor(), + padding_y: padding_y.floor(), + screen_lines, + cols, + } } #[inline] - pub fn padding_bottom(&self) -> usize { - (self.padding_y + (self.height - 2. * self.padding_y) % self.cell_height) as usize + pub fn reserve_lines(&mut self, count: usize) { + self.screen_lines = Line(max(self.screen_lines.saturating_sub(count), MIN_SCREEN_LINES)); } /// Check if coordinates are inside the terminal grid. /// - /// The padding is not counted as part of the grid. + /// The padding, message bar or search are not counted as part of the grid. #[inline] pub fn contains_point(&self, x: usize, y: usize) -> bool { - x < (self.width as usize - self.padding_right()) - && x >= self.padding_x as usize - && y < (self.height as usize - self.padding_bottom()) - && y >= self.padding_y as usize + x <= (self.padding_x + self.cols.0 as f32 * self.cell_width) as usize + && x > self.padding_x as usize + && y <= (self.padding_y + self.screen_lines.0 as f32 * self.cell_height) as usize + && y > self.padding_y as usize } + /// Convert window space pixels to terminal grid coordinates. + /// + /// If the coordinates are outside of the terminal grid, like positions inside the padding, the + /// coordinates will be clamped to the closest grid coordinates. 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)); Point { - line: min(line, Line(self.lines().saturating_sub(1))), - col: min(col, Column(self.cols().saturating_sub(1))), + line: min(line, Line(self.screen_lines.saturating_sub(1))), + col: min(col, Column(self.cols.saturating_sub(1))), } } + + #[inline] + pub fn width(&self) -> f32 { + self.width + } + + #[inline] + pub fn height(&self) -> f32 { + self.height + } + + #[inline] + pub fn cell_width(&self) -> f32 { + self.cell_width + } + + #[inline] + pub fn cell_height(&self) -> f32 { + self.cell_height + } + + #[inline] + pub fn padding_x(&self) -> f32 { + self.padding_x + } + + #[inline] + pub fn padding_y(&self) -> f32 { + self.padding_y + } + + #[inline] + pub fn screen_lines(&self) -> Line { + self.screen_lines + } + + #[inline] + pub fn cols(&self) -> Column { + self.cols + } + + /// Calculate padding to spread it evenly around the terminal content. + #[inline] + fn dynamic_padding(padding: f32, dimension: f32, cell_dimension: f32) -> f32 { + padding + ((dimension - 2. * padding) % cell_dimension) / 2. + } } pub struct Term<T> { @@ -751,8 +826,9 @@ pub struct Term<T> { /// Current forward and backward buffer search regexes. regex_search: Option<RegexSearch>, - /// Information about window dimensions. - size: SizeInfo, + /// Information about cell dimensions. + cell_width: usize, + cell_height: usize, } impl<T> Term<T> { @@ -767,8 +843,8 @@ impl<T> Term<T> { } pub fn new<C>(config: &Config<C>, size: SizeInfo, event_proxy: T) -> Term<T> { - let num_cols = size.cols(); - let num_lines = size.lines(); + let num_cols = size.cols; + let num_lines = size.screen_lines; let history_size = config.scrolling.history() as usize; let grid = Grid::new(num_lines, num_cols, history_size, Cell::default()); @@ -803,7 +879,8 @@ impl<T> Term<T> { title_stack: Vec::new(), selection: None, regex_search: None, - size, + cell_width: size.cell_width as usize, + cell_height: size.cell_height as usize, } } @@ -974,12 +1051,14 @@ impl<T> Term<T> { /// Resize terminal to new dimensions. pub fn resize(&mut self, size: SizeInfo) { - self.size = size; + self.cell_width = size.cell_width as usize; + self.cell_height = size.cell_height as usize; let old_cols = self.cols(); let old_lines = self.screen_lines(); - let num_cols = max(size.cols(), Column(MIN_SIZE)); - let num_lines = max(size.lines(), Line(MIN_SIZE)); + + let num_cols = size.cols; + let num_lines = size.screen_lines; if old_cols == num_cols && old_lines == num_lines { debug!("Term::resize dimensions unchanged"); @@ -2233,8 +2312,8 @@ impl<T: EventListener> Handler for Term<T> { #[inline] fn text_area_size_pixels<W: io::Write>(&mut self, writer: &mut W) { - let width = self.size.cell_width as usize * self.cols().0; - let height = self.size.cell_height as usize * self.screen_lines().0; + let width = self.cell_width * self.cols().0; + let height = self.cell_height * self.screen_lines().0; let _ = write!(writer, "\x1b[4;{};{}t", height, width); } @@ -2356,15 +2435,7 @@ pub mod test { .unwrap_or(0); // Create terminal with the appropriate dimensions. - let size = SizeInfo { - width: num_cols as f32, - height: lines.len() as f32, - cell_width: 1., - cell_height: 1., - padding_x: 0., - padding_y: 0., - dpr: 1., - }; + let size = SizeInfo::new(num_cols as f32, lines.len() as f32, 1., 1., 0., 0., false); let mut term = Term::new(&Config::<()>::default(), size, ()); // Fill terminal with content. @@ -2413,15 +2484,7 @@ mod tests { #[test] fn semantic_selection_works() { - let size = SizeInfo { - width: 21.0, - height: 51.0, - cell_width: 3.0, - cell_height: 3.0, - padding_x: 0.0, - padding_y: 0.0, - dpr: 1.0, - }; + let size = SizeInfo::new(21.0, 51.0, 3.0, 3.0, 0.0, 0.0, false); let mut term = Term::new(&MockConfig::default(), size, Mock); let mut grid: Grid<Cell> = Grid::new(Line(3), Column(5), 0, Cell::default()); for i in 0..5 { @@ -2469,15 +2532,7 @@ mod tests { #[test] fn line_selection_works() { - let size = SizeInfo { - width: 21.0, - height: 51.0, - cell_width: 3.0, - cell_height: 3.0, - padding_x: 0.0, - padding_y: 0.0, - dpr: 1.0, - }; + let size = SizeInfo::new(21.0, 51.0, 3.0, 3.0, 0.0, 0.0, false); let mut term = Term::new(&MockConfig::default(), size, Mock); let mut grid: Grid<Cell> = Grid::new(Line(1), Column(5), 0, Cell::default()); for i in 0..5 { @@ -2498,15 +2553,7 @@ mod tests { #[test] fn selecting_empty_line() { - let size = SizeInfo { - width: 21.0, - height: 51.0, - cell_width: 3.0, - cell_height: 3.0, - padding_x: 0.0, - padding_y: 0.0, - dpr: 1.0, - }; + let size = SizeInfo::new(21.0, 51.0, 3.0, 3.0, 0.0, 0.0, false); let mut term = Term::new(&MockConfig::default(), size, Mock); let mut grid: Grid<Cell> = Grid::new(Line(3), Column(3), 0, Cell::default()); for l in 0..3 { @@ -2543,15 +2590,7 @@ mod tests { #[test] fn input_line_drawing_character() { - let size = SizeInfo { - width: 21.0, - height: 51.0, - cell_width: 3.0, - cell_height: 3.0, - padding_x: 0.0, - padding_y: 0.0, - dpr: 1.0, - }; + let size = SizeInfo::new(21.0, 51.0, 3.0, 3.0, 0.0, 0.0, false); let mut term = Term::new(&MockConfig::default(), size, Mock); let cursor = Point::new(Line(0), Column(0)); term.configure_charset(CharsetIndex::G0, StandardCharset::SpecialCharacterAndLineDrawing); @@ -2562,15 +2601,7 @@ mod tests { #[test] fn clear_saved_lines() { - let size = SizeInfo { - width: 21.0, - height: 51.0, - cell_width: 3.0, - cell_height: 3.0, - padding_x: 0.0, - padding_y: 0.0, - dpr: 1.0, - }; + let size = SizeInfo::new(21.0, 51.0, 3.0, 3.0, 0.0, 0.0, false); let mut term = Term::new(&MockConfig::default(), size, Mock); // Add one line of scrollback. @@ -2592,15 +2623,7 @@ mod tests { #[test] fn grow_lines_updates_active_cursor_pos() { - let mut size = SizeInfo { - width: 100.0, - height: 10.0, - cell_width: 1.0, - cell_height: 1.0, - padding_x: 0.0, - padding_y: 0.0, - dpr: 1.0, - }; + let mut size = SizeInfo::new(100.0, 10.0, 1.0, 1.0, 0.0, 0.0, false); let mut term = Term::new(&MockConfig::default(), size, Mock); // Create 10 lines of scrollback. @@ -2611,7 +2634,7 @@ mod tests { assert_eq!(term.grid.cursor.point, Point::new(Line(9), Column(0))); // Increase visible lines. - size.height = 30.; + size.screen_lines.0 = 30; term.resize(size); assert_eq!(term.history_size(), 0); @@ -2620,15 +2643,7 @@ mod tests { #[test] fn grow_lines_updates_inactive_cursor_pos() { - let mut size = SizeInfo { - width: 100.0, - height: 10.0, - cell_width: 1.0, - cell_height: 1.0, - padding_x: 0.0, - padding_y: 0.0, - dpr: 1.0, - }; + let mut size = SizeInfo::new(100.0, 10.0, 1.0, 1.0, 0.0, 0.0, false); let mut term = Term::new(&MockConfig::default(), size, Mock); // Create 10 lines of scrollback. @@ -2642,7 +2657,7 @@ mod tests { term.set_mode(ansi::Mode::SwapScreenAndSetRestoreCursor); // Increase visible lines. - size.height = 30.; + size.screen_lines.0 = 30; term.resize(size); // Leave alt screen. @@ -2654,15 +2669,7 @@ mod tests { #[test] fn shrink_lines_updates_active_cursor_pos() { - let mut size = SizeInfo { - width: 100.0, - height: 10.0, - cell_width: 1.0, - cell_height: 1.0, - padding_x: 0.0, - padding_y: 0.0, - dpr: 1.0, - }; + let mut size = SizeInfo::new(100.0, 10.0, 1.0, 1.0, 0.0, 0.0, false); let mut term = Term::new(&MockConfig::default(), size, Mock); // Create 10 lines of scrollback. @@ -2673,7 +2680,7 @@ mod tests { assert_eq!(term.grid.cursor.point, Point::new(Line(9), Column(0))); // Increase visible lines. - size.height = 5.; + size.screen_lines.0 = 5; term.resize(size); assert_eq!(term.history_size(), 15); @@ -2682,15 +2689,7 @@ mod tests { #[test] fn shrink_lines_updates_inactive_cursor_pos() { - let mut size = SizeInfo { - width: 100.0, - height: 10.0, - cell_width: 1.0, - cell_height: 1.0, - padding_x: 0.0, - padding_y: 0.0, - dpr: 1.0, - }; + let mut size = SizeInfo::new(100.0, 10.0, 1.0, 1.0, 0.0, 0.0, false); let mut term = Term::new(&MockConfig::default(), size, Mock); // Create 10 lines of scrollback. @@ -2704,7 +2703,7 @@ mod tests { term.set_mode(ansi::Mode::SwapScreenAndSetRestoreCursor); // Increase visible lines. - size.height = 5.; + size.screen_lines.0 = 5; term.resize(size); // Leave alt screen. @@ -2716,15 +2715,7 @@ mod tests { #[test] fn window_title() { - let size = SizeInfo { - width: 21.0, - height: 51.0, - cell_width: 3.0, - cell_height: 3.0, - padding_x: 0.0, - padding_y: 0.0, - dpr: 1.0, - }; + let size = SizeInfo::new(21.0, 51.0, 3.0, 3.0, 0.0, 0.0, false); let mut term = Term::new(&MockConfig::default(), size, Mock); // Title None by default. |