term_model/term/
mod.rs

1// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15//! Exports the `Term` type which is a high-level API for the Grid
16use std::cmp::{max, min};
17use std::ops::{Index, IndexMut, Range};
18use std::time::{Duration, Instant};
19use std::{io, mem, ptr, str};
20
21use log::{debug, trace};
22use serde::{Deserialize, Serialize};
23use unicode_width::UnicodeWidthChar;
24
25use crate::ansi::{
26    self, Attr, CharsetIndex, Color, CursorStyle, Handler, NamedColor, StandardCharset, TermInfo,
27};
28use crate::clipboard::{Clipboard, ClipboardType};
29use crate::config::{Config, VisualBellAnimation, DEFAULT_NAME};
30use crate::event::{Event, EventListener};
31use crate::grid::{
32    BidirectionalIterator, DisplayIter, Grid, GridCell, IndexRegion, Indexed, Scroll,
33};
34use crate::index::{self, Column, IndexRange, Line, Point};
35use crate::selection::{Selection, SelectionRange};
36use crate::term::cell::{Cell, Flags, LineLength};
37use crate::term::color::Rgb;
38
39pub mod cell;
40pub mod color;
41
42/// Used to match equal brackets, when performing a bracket-pair selection.
43const BRACKET_PAIRS: [(char, char); 4] = [('(', ')'), ('[', ']'), ('{', '}'), ('<', '>')];
44
45/// Max size of the window title stack
46const TITLE_STACK_MAX_DEPTH: usize = 4096;
47
48/// A type that can expand a given point to a region
49///
50/// Usually this is implemented for some 2-D array type since
51/// points are two dimensional indices.
52pub trait Search {
53    /// Find the nearest semantic boundary _to the left_ of provided point.
54    fn semantic_search_left(&self, _: Point<usize>) -> Point<usize>;
55    /// Find the nearest semantic boundary _to the point_ of provided point.
56    fn semantic_search_right(&self, _: Point<usize>) -> Point<usize>;
57    /// Find the beginning of a line, following line wraps.
58    fn line_search_left(&self, _: Point<usize>) -> Point<usize>;
59    /// Find the end of a line, following line wraps.
60    fn line_search_right(&self, _: Point<usize>) -> Point<usize>;
61    /// Find the nearest matching bracket.
62    fn bracket_search(&self, _: Point<usize>) -> Option<Point<usize>>;
63}
64
65impl<T> Search for Term<T> {
66    fn semantic_search_left(&self, mut point: Point<usize>) -> Point<usize> {
67        // Limit the starting point to the last line in the history
68        point.line = min(point.line, self.grid.len() - 1);
69
70        let mut iter = self.grid.iter_from(point);
71        let last_col = self.grid.num_cols() - Column(1);
72
73        while let Some(cell) = iter.prev() {
74            if !cell.flags.intersects(Flags::WIDE_CHAR | Flags::WIDE_CHAR_SPACER)
75                && self.semantic_escape_chars.contains(cell.c)
76            {
77                break;
78            }
79
80            if iter.point().col == last_col && !cell.flags.contains(Flags::WRAPLINE) {
81                break; // cut off if on new line or hit escape char
82            }
83
84            point = iter.point();
85        }
86
87        point
88    }
89
90    fn semantic_search_right(&self, mut point: Point<usize>) -> Point<usize> {
91        // Limit the starting point to the last line in the history
92        point.line = min(point.line, self.grid.len() - 1);
93
94        let mut iter = self.grid.iter_from(point);
95        let last_col = self.grid.num_cols() - 1;
96
97        while let Some(cell) = iter.next() {
98            if !cell.flags.intersects(Flags::WIDE_CHAR | Flags::WIDE_CHAR_SPACER)
99                && self.semantic_escape_chars.contains(cell.c)
100            {
101                break;
102            }
103
104            point = iter.point();
105
106            if point.col == last_col && !cell.flags.contains(Flags::WRAPLINE) {
107                break; // cut off if on new line or hit escape char
108            }
109        }
110
111        point
112    }
113
114    fn line_search_left(&self, mut point: Point<usize>) -> Point<usize> {
115        while point.line + 1 < self.grid.len()
116            && self.grid[point.line + 1][self.grid.num_cols() - 1].flags.contains(Flags::WRAPLINE)
117        {
118            point.line += 1;
119        }
120
121        point.col = Column(0);
122
123        point
124    }
125
126    fn line_search_right(&self, mut point: Point<usize>) -> Point<usize> {
127        while self.grid[point.line][self.grid.num_cols() - 1].flags.contains(Flags::WRAPLINE) {
128            point.line -= 1;
129        }
130
131        point.col = self.grid.num_cols() - 1;
132
133        point
134    }
135
136    fn bracket_search(&self, point: Point<usize>) -> Option<Point<usize>> {
137        let start_char = self.grid[point.line][point.col].c;
138
139        // Find the matching bracket we're looking for
140        let (forwards, end_char) = BRACKET_PAIRS.iter().find_map(|(open, close)| {
141            if open == &start_char {
142                Some((true, *close))
143            } else if close == &start_char {
144                Some((false, *open))
145            } else {
146                None
147            }
148        })?;
149
150        let mut iter = self.grid.iter_from(point);
151
152        // For every character match that equals the starting bracket, we
153        // ignore one bracket of the opposite type.
154        let mut skip_pairs = 0;
155
156        loop {
157            // Check the next cell
158            let cell = if forwards { iter.next() } else { iter.prev() };
159
160            // Break if there are no more cells
161            let c = match cell {
162                Some(cell) => cell.c,
163                None => break,
164            };
165
166            // Check if the bracket matches
167            if c == end_char && skip_pairs == 0 {
168                return Some(iter.point());
169            } else if c == start_char {
170                skip_pairs += 1;
171            } else if c == end_char {
172                skip_pairs -= 1;
173            }
174        }
175
176        None
177    }
178}
179
180/// A key for caching cursor glyphs
181#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash, Deserialize)]
182pub struct CursorKey {
183    pub style: CursorStyle,
184    pub is_wide: bool,
185}
186
187/// Iterator that yields cells needing render
188///
189/// Yields cells that require work to be displayed (that is, not a an empty
190/// background cell). Additionally, this manages some state of the grid only
191/// relevant for rendering like temporarily changing the cell with the cursor.
192///
193/// This manages the cursor during a render. The cursor location is inverted to
194/// draw it, and reverted after drawing to maintain state.
195pub struct RenderableCellsIter<'a, C> {
196    inner: DisplayIter<'a, Cell>,
197    grid: &'a Grid<Cell>,
198    cursor: &'a Point,
199    cursor_offset: usize,
200    cursor_key: Option<CursorKey>,
201    cursor_style: CursorStyle,
202    config: &'a Config<C>,
203    colors: &'a color::List,
204    selection: Option<SelectionRange<Line>>,
205}
206
207impl<'a, C> RenderableCellsIter<'a, C> {
208    /// Create the renderable cells iterator
209    ///
210    /// The cursor and terminal mode are required for properly displaying the
211    /// cursor.
212    fn new<'b, T>(
213        term: &'b Term<T>,
214        config: &'b Config<C>,
215        selection: Option<SelectionRange>,
216        mut cursor_style: CursorStyle,
217    ) -> RenderableCellsIter<'b, C> {
218        let grid = &term.grid;
219        let num_cols = grid.num_cols();
220        let num_lines = grid.num_lines();
221
222        let cursor_offset = grid.line_to_offset(term.cursor.point.line);
223        let inner = grid.display_iter();
224
225        let selection_range = selection.and_then(|span| {
226            let (limit_start, limit_end) = if span.is_block {
227                (span.start.col, span.end.col)
228            } else {
229                (Column(0), num_cols - 1)
230            };
231
232            // Get on-screen lines of the selection's locations
233            let start = term.buffer_to_visible(span.start);
234            let end = term.buffer_to_visible(span.end);
235
236            // Clamp visible selection to the viewport
237            let (mut start, mut end) = match (start, end) {
238                (Some(start), Some(end)) => (start, end),
239                (Some(start), None) => {
240                    let end = Point::new(num_lines.0 - 1, num_cols - 1);
241                    (start, end)
242                },
243                (None, Some(end)) => {
244                    let start = Point::new(0, Column(0));
245                    (start, end)
246                },
247                (None, None) => return None,
248            };
249
250            // Trim start/end with partially visible block selection
251            start.col = max(limit_start, start.col);
252            end.col = min(limit_end, end.col);
253
254            Some(SelectionRange::new(start.into(), end.into(), span.is_block))
255        });
256
257        // Load cursor glyph
258        let cursor = &term.cursor.point;
259        let cursor_visible = term.mode.contains(TermMode::SHOW_CURSOR) && grid.contains(cursor);
260        let cursor_key = if cursor_visible {
261            let is_wide =
262                grid[cursor].flags.contains(Flags::WIDE_CHAR) && (cursor.col + 1) < num_cols;
263            Some(CursorKey { style: cursor_style, is_wide })
264        } else {
265            // Use hidden cursor so text will not get inverted
266            cursor_style = CursorStyle::Hidden;
267            None
268        };
269
270        RenderableCellsIter {
271            cursor,
272            cursor_offset,
273            grid,
274            inner,
275            selection: selection_range,
276            config,
277            colors: &term.colors,
278            cursor_key,
279            cursor_style,
280        }
281    }
282}
283
284#[derive(Copy, Clone, Debug)]
285pub enum RenderableCellContent {
286    Chars([char; cell::MAX_ZEROWIDTH_CHARS + 1]),
287    Cursor(CursorKey),
288}
289
290#[derive(Copy, Clone, Debug)]
291pub struct RenderableCell {
292    /// A _Display_ line (not necessarily an _Active_ line)
293    pub line: Line,
294    pub column: Column,
295    pub inner: RenderableCellContent,
296    pub fg: Rgb,
297    pub bg: Rgb,
298    pub bg_alpha: f32,
299    pub flags: Flags,
300}
301
302impl RenderableCell {
303    fn new<C>(
304        config: &Config<C>,
305        colors: &color::List,
306        cell: Indexed<Cell>,
307        selected: bool,
308    ) -> Self {
309        // Lookup RGB values
310        let mut fg_rgb = Self::compute_fg_rgb(config, colors, cell.fg, cell.flags);
311        let mut bg_rgb = Self::compute_bg_rgb(colors, cell.bg);
312        let mut bg_alpha = Self::compute_bg_alpha(cell.bg);
313
314        let selection_background = config.colors.selection.background;
315        if let (true, Some(col)) = (selected, selection_background) {
316            // Override selection background with config colors
317            bg_rgb = col;
318            bg_alpha = 1.0;
319        } else if selected ^ cell.inverse() {
320            if fg_rgb == bg_rgb && !cell.flags.contains(Flags::HIDDEN) {
321                // Reveal inversed text when fg/bg is the same
322                fg_rgb = colors[NamedColor::Background];
323                bg_rgb = colors[NamedColor::Foreground];
324            } else {
325                // Invert cell fg and bg colors
326                mem::swap(&mut fg_rgb, &mut bg_rgb);
327            }
328
329            bg_alpha = 1.0;
330        }
331
332        // Override selection text with config colors
333        if let (true, Some(col)) = (selected, config.colors.selection.text) {
334            fg_rgb = col;
335        }
336
337        RenderableCell {
338            line: cell.line,
339            column: cell.column,
340            inner: RenderableCellContent::Chars(cell.chars()),
341            fg: fg_rgb,
342            bg: bg_rgb,
343            bg_alpha,
344            flags: cell.flags,
345        }
346    }
347
348    fn compute_fg_rgb<C>(config: &Config<C>, colors: &color::List, fg: Color, flags: Flags) -> Rgb {
349        match fg {
350            Color::Spec(rgb) => rgb,
351            Color::Named(ansi) => {
352                match (config.draw_bold_text_with_bright_colors(), flags & Flags::DIM_BOLD) {
353                    // If no bright foreground is set, treat it like the BOLD flag doesn't exist
354                    (_, Flags::DIM_BOLD)
355                        if ansi == NamedColor::Foreground
356                            && config.colors.primary.bright_foreground.is_none() =>
357                    {
358                        colors[NamedColor::DimForeground]
359                    },
360                    // Draw bold text in bright colors *and* contains bold flag.
361                    (true, Flags::BOLD) => colors[ansi.to_bright()],
362                    // Cell is marked as dim and not bold
363                    (_, Flags::DIM) | (false, Flags::DIM_BOLD) => colors[ansi.to_dim()],
364                    // None of the above, keep original color.
365                    _ => colors[ansi],
366                }
367            },
368            Color::Indexed(idx) => {
369                let idx = match (
370                    config.draw_bold_text_with_bright_colors(),
371                    flags & Flags::DIM_BOLD,
372                    idx,
373                ) {
374                    (true, Flags::BOLD, 0..=7) => idx as usize + 8,
375                    (false, Flags::DIM, 8..=15) => idx as usize - 8,
376                    (false, Flags::DIM, 0..=7) => idx as usize + 260,
377                    _ => idx as usize,
378                };
379
380                colors[idx]
381            },
382        }
383    }
384
385    #[inline]
386    fn compute_bg_alpha(bg: Color) -> f32 {
387        if bg == Color::Named(NamedColor::Background) {
388            0.
389        } else {
390            1.
391        }
392    }
393
394    #[inline]
395    fn compute_bg_rgb(colors: &color::List, bg: Color) -> Rgb {
396        match bg {
397            Color::Spec(rgb) => rgb,
398            Color::Named(ansi) => colors[ansi],
399            Color::Indexed(idx) => colors[idx],
400        }
401    }
402}
403
404impl<'a, C> Iterator for RenderableCellsIter<'a, C> {
405    type Item = RenderableCell;
406
407    /// Gets the next renderable cell
408    ///
409    /// Skips empty (background) cells and applies any flags to the cell state
410    /// (eg. invert fg and bg colors).
411    #[inline]
412    fn next(&mut self) -> Option<Self::Item> {
413        loop {
414            if self.cursor_offset == self.inner.offset() && self.inner.column() == self.cursor.col {
415                let selected = self
416                    .selection
417                    .as_ref()
418                    .map(|range| range.contains(self.cursor.col, self.cursor.line))
419                    .unwrap_or(false);
420
421                // Handle cursor
422                if let Some(cursor_key) = self.cursor_key.take() {
423                    let cell = Indexed {
424                        inner: self.grid[self.cursor],
425                        column: self.cursor.col,
426                        // Using `self.cursor.line` leads to inconsitent cursor position when
427                        // scrolling. See https://github.com/alacritty/alacritty/issues/2570 for more
428                        // info.
429                        line: self.inner.line(),
430                    };
431
432                    let mut renderable_cell =
433                        RenderableCell::new(self.config, self.colors, cell, selected);
434
435                    renderable_cell.inner = RenderableCellContent::Cursor(cursor_key);
436
437                    if let Some(color) = self.config.cursor_cursor_color() {
438                        renderable_cell.fg = RenderableCell::compute_bg_rgb(self.colors, color);
439                    }
440
441                    return Some(renderable_cell);
442                } else {
443                    let mut cell =
444                        RenderableCell::new(self.config, self.colors, self.inner.next()?, selected);
445
446                    if self.cursor_style == CursorStyle::Block {
447                        std::mem::swap(&mut cell.bg, &mut cell.fg);
448
449                        if let Some(color) = self.config.cursor_text_color() {
450                            cell.fg = color;
451                        }
452                    }
453
454                    return Some(cell);
455                }
456            } else {
457                let cell = self.inner.next()?;
458
459                let selected = self
460                    .selection
461                    .as_ref()
462                    .map(|range| range.contains(cell.column, cell.line))
463                    .unwrap_or(false);
464
465                if !cell.is_empty() || selected {
466                    return Some(RenderableCell::new(self.config, self.colors, cell, selected));
467                }
468            }
469        }
470    }
471}
472
473pub mod mode {
474    // Part of the code for the NONE case that's produced by the macro triggers the lint, but as a
475    // whole, the produced code is still correct.
476    #![allow(clippy::bad_bit_mask)] // TODO(b/303500202) Remove once addressed in bitflags.
477    use bitflags::bitflags;
478
479    bitflags! {
480        pub struct TermMode: u16 {
481            const SHOW_CURSOR         = 0b0000_0000_0000_0001;
482            const APP_CURSOR          = 0b0000_0000_0000_0010;
483            const APP_KEYPAD          = 0b0000_0000_0000_0100;
484            const MOUSE_REPORT_CLICK  = 0b0000_0000_0000_1000;
485            const BRACKETED_PASTE     = 0b0000_0000_0001_0000;
486            const SGR_MOUSE           = 0b0000_0000_0010_0000;
487            const MOUSE_MOTION        = 0b0000_0000_0100_0000;
488            const LINE_WRAP           = 0b0000_0000_1000_0000;
489            const LINE_FEED_NEW_LINE  = 0b0000_0001_0000_0000;
490            const ORIGIN              = 0b0000_0010_0000_0000;
491            const INSERT              = 0b0000_0100_0000_0000;
492            const FOCUS_IN_OUT        = 0b0000_1000_0000_0000;
493            const ALT_SCREEN          = 0b0001_0000_0000_0000;
494            const MOUSE_DRAG          = 0b0010_0000_0000_0000;
495            const MOUSE_MODE          = 0b0010_0000_0100_1000;
496            const UTF8_MOUSE          = 0b0100_0000_0000_0000;
497            const ALTERNATE_SCROLL    = 0b1000_0000_0000_0000;
498            const ANY                 = 0b1111_1111_1111_1111;
499            const NONE                = 0;
500        }
501    }
502
503    impl Default for TermMode {
504        fn default() -> TermMode {
505            TermMode::SHOW_CURSOR | TermMode::LINE_WRAP | TermMode::ALTERNATE_SCROLL
506        }
507    }
508}
509
510pub use crate::term::mode::TermMode;
511
512trait CharsetMapping {
513    fn map(&self, c: char) -> char {
514        c
515    }
516}
517
518impl CharsetMapping for StandardCharset {
519    /// Switch/Map character to the active charset. Ascii is the common case and
520    /// for that we want to do as little as possible.
521    #[inline]
522    fn map(&self, c: char) -> char {
523        match *self {
524            StandardCharset::Ascii => c,
525            StandardCharset::SpecialCharacterAndLineDrawing => match c {
526                '`' => '◆',
527                'a' => '▒',
528                'b' => '\t',
529                'c' => '\u{000c}',
530                'd' => '\r',
531                'e' => '\n',
532                'f' => '°',
533                'g' => '±',
534                'h' => '\u{2424}',
535                'i' => '\u{000b}',
536                'j' => '┘',
537                'k' => '┐',
538                'l' => '┌',
539                'm' => '└',
540                'n' => '┼',
541                'o' => '⎺',
542                'p' => '⎻',
543                'q' => '─',
544                'r' => '⎼',
545                's' => '⎽',
546                't' => '├',
547                'u' => '┤',
548                'v' => '┴',
549                'w' => '┬',
550                'x' => '│',
551                'y' => '≤',
552                'z' => '≥',
553                '{' => 'π',
554                '|' => '≠',
555                '}' => '£',
556                '~' => '·',
557                _ => c,
558            },
559        }
560    }
561}
562
563#[derive(Default, Copy, Clone)]
564struct Charsets([StandardCharset; 4]);
565
566impl Index<CharsetIndex> for Charsets {
567    type Output = StandardCharset;
568
569    fn index(&self, index: CharsetIndex) -> &StandardCharset {
570        &self.0[index as usize]
571    }
572}
573
574impl IndexMut<CharsetIndex> for Charsets {
575    fn index_mut(&mut self, index: CharsetIndex) -> &mut StandardCharset {
576        &mut self.0[index as usize]
577    }
578}
579
580#[derive(Default, Copy, Clone)]
581pub struct Cursor {
582    /// The location of this cursor
583    pub point: Point,
584
585    /// Template cell when using this cursor
586    template: Cell,
587
588    /// Currently configured graphic character sets
589    charsets: Charsets,
590}
591
592pub struct VisualBell {
593    /// Visual bell animation
594    animation: VisualBellAnimation,
595
596    /// Visual bell duration
597    duration: Duration,
598
599    /// The last time the visual bell rang, if at all
600    start_time: Option<Instant>,
601}
602
603fn cubic_bezier(p0: f64, p1: f64, p2: f64, p3: f64, x: f64) -> f64 {
604    (1.0 - x).powi(3) * p0
605        + 3.0 * (1.0 - x).powi(2) * x * p1
606        + 3.0 * (1.0 - x) * x.powi(2) * p2
607        + x.powi(3) * p3
608}
609
610impl VisualBell {
611    pub fn new<C>(config: &Config<C>) -> VisualBell {
612        let visual_bell_config = &config.visual_bell;
613        VisualBell {
614            animation: visual_bell_config.animation,
615            duration: visual_bell_config.duration(),
616            start_time: None,
617        }
618    }
619
620    /// Ring the visual bell, and return its intensity.
621    pub fn ring(&mut self) -> f64 {
622        let now = Instant::now();
623        self.start_time = Some(now);
624        self.intensity_at_instant(now)
625    }
626
627    /// Get the currently intensity of the visual bell. The bell's intensity
628    /// ramps down from 1.0 to 0.0 at a rate determined by the bell's duration.
629    pub fn intensity(&self) -> f64 {
630        self.intensity_at_instant(Instant::now())
631    }
632
633    /// Check whether or not the visual bell has completed "ringing".
634    pub fn completed(&mut self) -> bool {
635        match self.start_time {
636            Some(earlier) => {
637                if Instant::now().duration_since(earlier) >= self.duration {
638                    self.start_time = None;
639                }
640                false
641            },
642            None => true,
643        }
644    }
645
646    /// Get the intensity of the visual bell at a particular instant. The bell's
647    /// intensity ramps down from 1.0 to 0.0 at a rate determined by the bell's
648    /// duration.
649    pub fn intensity_at_instant(&self, instant: Instant) -> f64 {
650        // If `duration` is zero, then the VisualBell is disabled; therefore,
651        // its `intensity` is zero.
652        if self.duration == Duration::from_secs(0) {
653            return 0.0;
654        }
655
656        match self.start_time {
657            // Similarly, if `start_time` is `None`, then the VisualBell has not
658            // been "rung"; therefore, its `intensity` is zero.
659            None => 0.0,
660
661            Some(earlier) => {
662                // Finally, if the `instant` at which we wish to compute the
663                // VisualBell's `intensity` occurred before the VisualBell was
664                // "rung", then its `intensity` is also zero.
665                if instant < earlier {
666                    return 0.0;
667                }
668
669                let elapsed = instant.duration_since(earlier);
670                let elapsed_f =
671                    elapsed.as_secs() as f64 + f64::from(elapsed.subsec_nanos()) / 1e9f64;
672                let duration_f = self.duration.as_secs() as f64
673                    + f64::from(self.duration.subsec_nanos()) / 1e9f64;
674
675                // Otherwise, we compute a value `time` from 0.0 to 1.0
676                // inclusive that represents the ratio of `elapsed` time to the
677                // `duration` of the VisualBell.
678                let time = (elapsed_f / duration_f).min(1.0);
679
680                // We use this to compute the inverse `intensity` of the
681                // VisualBell. When `time` is 0.0, `inverse_intensity` is 0.0,
682                // and when `time` is 1.0, `inverse_intensity` is 1.0.
683                let inverse_intensity = match self.animation {
684                    VisualBellAnimation::Ease | VisualBellAnimation::EaseOut => {
685                        cubic_bezier(0.25, 0.1, 0.25, 1.0, time)
686                    },
687                    VisualBellAnimation::EaseOutSine => cubic_bezier(0.39, 0.575, 0.565, 1.0, time),
688                    VisualBellAnimation::EaseOutQuad => cubic_bezier(0.25, 0.46, 0.45, 0.94, time),
689                    VisualBellAnimation::EaseOutCubic => {
690                        cubic_bezier(0.215, 0.61, 0.355, 1.0, time)
691                    },
692                    VisualBellAnimation::EaseOutQuart => cubic_bezier(0.165, 0.84, 0.44, 1.0, time),
693                    VisualBellAnimation::EaseOutQuint => cubic_bezier(0.23, 1.0, 0.32, 1.0, time),
694                    VisualBellAnimation::EaseOutExpo => cubic_bezier(0.19, 1.0, 0.22, 1.0, time),
695                    VisualBellAnimation::EaseOutCirc => cubic_bezier(0.075, 0.82, 0.165, 1.0, time),
696                    VisualBellAnimation::Linear => time,
697                };
698
699                // Since we want the `intensity` of the VisualBell to decay over
700                // `time`, we subtract the `inverse_intensity` from 1.0.
701                1.0 - inverse_intensity
702            },
703        }
704    }
705
706    pub fn update_config<C>(&mut self, config: &Config<C>) {
707        let visual_bell_config = &config.visual_bell;
708        self.animation = visual_bell_config.animation;
709        self.duration = visual_bell_config.duration();
710    }
711}
712
713pub struct Term<T> {
714    /// Terminal focus
715    pub is_focused: bool,
716
717    /// The grid
718    grid: Grid<Cell>,
719
720    /// Tracks if the next call to input will need to first handle wrapping.
721    /// This is true after the last column is set with the input function. Any function that
722    /// implicitly sets the line or column needs to set this to false to avoid wrapping twice.
723    /// input_needs_wrap ensures that cursor.col is always valid for use into indexing into
724    /// arrays. Without it we would have to sanitize cursor.col every time we used it.
725    input_needs_wrap: bool,
726
727    /// Alternate grid
728    alt_grid: Grid<Cell>,
729
730    /// Alt is active
731    alt: bool,
732
733    /// The cursor
734    cursor: Cursor,
735
736    /// The graphic character set, out of `charsets`, which ASCII is currently
737    /// being mapped to
738    active_charset: CharsetIndex,
739
740    /// Tabstops
741    tabs: TabStops,
742
743    /// Mode flags
744    mode: TermMode,
745
746    /// Scroll region.
747    ///
748    /// Range going from top to bottom of the terminal, indexed from the top of the viewport.
749    scroll_region: Range<Line>,
750
751    pub dirty: bool,
752
753    pub visual_bell: VisualBell,
754
755    /// Saved cursor from main grid
756    cursor_save: Cursor,
757
758    /// Saved cursor from alt grid
759    cursor_save_alt: Cursor,
760
761    semantic_escape_chars: String,
762
763    /// Colors used for rendering
764    colors: color::List,
765
766    /// Is color in `colors` modified or not
767    color_modified: [bool; color::COUNT],
768
769    /// Original colors from config
770    original_colors: color::List,
771
772    /// Current style of the cursor
773    cursor_style: Option<CursorStyle>,
774
775    /// Default style for resetting the cursor
776    default_cursor_style: CursorStyle,
777
778    /// Whether to permit updating the terminal title
779    dynamic_title: bool,
780
781    /// Number of spaces in one tab
782    tabspaces: usize,
783
784    /// Clipboard access coupled to the active window
785    clipboard: Clipboard,
786
787    /// Proxy for sending events to the event loop
788    event_proxy: T,
789
790    /// Current title of the window
791    title: String,
792
793    /// Stack of saved window titles. When a title is popped from this stack, the `title` for the
794    /// term is set, and the Glutin window's title attribute is changed through the event listener.
795    title_stack: Vec<String>,
796}
797
798/// Terminal size info
799#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)]
800pub struct SizeInfo {
801    /// Terminal window width
802    pub width: f32,
803
804    /// Terminal window height
805    pub height: f32,
806
807    /// Width of individual cell
808    pub cell_width: f32,
809
810    /// Height of individual cell
811    pub cell_height: f32,
812
813    /// Horizontal window padding
814    pub padding_x: f32,
815
816    /// Horizontal window padding
817    pub padding_y: f32,
818
819    /// DPI factor of the current window
820    #[serde(default)]
821    pub dpr: f64,
822}
823
824impl SizeInfo {
825    #[inline]
826    pub fn lines(&self) -> Line {
827        Line(((self.height - 2. * self.padding_y) / self.cell_height) as usize)
828    }
829
830    #[inline]
831    pub fn cols(&self) -> Column {
832        Column(((self.width - 2. * self.padding_x) / self.cell_width) as usize)
833    }
834
835    /// Check if coordinates are inside the terminal grid.
836    ///
837    /// The padding is not counted as part of the grid.
838    pub fn contains_point(&self, x: usize, y: usize) -> bool {
839        x < (self.width - self.padding_x) as usize
840            && x >= self.padding_x as usize
841            && y < (self.height - self.padding_y) as usize
842            && y >= self.padding_y as usize
843    }
844
845    pub fn pixels_to_coords(&self, x: usize, y: usize) -> Point {
846        let col = Column(x.saturating_sub(self.padding_x as usize) / (self.cell_width as usize));
847        let line = Line(y.saturating_sub(self.padding_y as usize) / (self.cell_height as usize));
848
849        Point {
850            line: min(line, Line(self.lines().saturating_sub(1))),
851            col: min(col, Column(self.cols().saturating_sub(1))),
852        }
853    }
854}
855
856impl<T> Term<T> {
857    pub fn selection(&self) -> &Option<Selection> {
858        &self.grid.selection
859    }
860
861    pub fn selection_mut(&mut self) -> &mut Option<Selection> {
862        &mut self.grid.selection
863    }
864
865    #[inline]
866    pub fn scroll_display(&mut self, scroll: Scroll)
867    where
868        T: EventListener,
869    {
870        self.event_proxy.send_event(Event::MouseCursorDirty);
871        self.grid.scroll_display(scroll);
872        self.dirty = true;
873    }
874
875    pub fn new<C>(
876        config: &Config<C>,
877        size: &SizeInfo,
878        clipboard: Clipboard,
879        event_proxy: T,
880    ) -> Term<T> {
881        let num_cols = size.cols();
882        let num_lines = size.lines();
883
884        let history_size = config.scrolling.history() as usize;
885        let grid = Grid::new(num_lines, num_cols, history_size, Cell::default());
886        let alt = Grid::new(num_lines, num_cols, 0 /* scroll history */, Cell::default());
887
888        let tabspaces = config.tabspaces();
889        let tabs = TabStops::new(grid.num_cols(), tabspaces);
890
891        let scroll_region = Line(0)..grid.num_lines();
892
893        let colors = color::List::from(&config.colors);
894
895        Term {
896            dirty: false,
897            visual_bell: VisualBell::new(config),
898            input_needs_wrap: false,
899            grid,
900            alt_grid: alt,
901            alt: false,
902            active_charset: Default::default(),
903            cursor: Default::default(),
904            cursor_save: Default::default(),
905            cursor_save_alt: Default::default(),
906            tabs,
907            mode: Default::default(),
908            scroll_region,
909            colors,
910            color_modified: [false; color::COUNT],
911            original_colors: colors,
912            semantic_escape_chars: config.selection.semantic_escape_chars().to_owned(),
913            cursor_style: None,
914            default_cursor_style: config.cursor.style,
915            dynamic_title: config.dynamic_title(),
916            tabspaces,
917            clipboard,
918            event_proxy,
919            is_focused: true,
920            title: config.window.title.clone(),
921            title_stack: Vec::new(),
922        }
923    }
924
925    pub fn update_config<C>(&mut self, config: &Config<C>) {
926        self.semantic_escape_chars = config.selection.semantic_escape_chars().to_owned();
927        self.original_colors.fill_named(&config.colors);
928        self.original_colors.fill_cube(&config.colors);
929        self.original_colors.fill_gray_ramp(&config.colors);
930        for i in 0..color::COUNT {
931            if !self.color_modified[i] {
932                self.colors[i] = self.original_colors[i];
933            }
934        }
935        self.visual_bell.update_config(config);
936        if let Some(0) = config.scrolling.faux_multiplier() {
937            self.mode.remove(TermMode::ALTERNATE_SCROLL);
938        }
939        self.default_cursor_style = config.cursor.style;
940        self.dynamic_title = config.dynamic_title();
941        self.grid.update_history(config.scrolling.history() as usize);
942    }
943
944    /// Convert the active selection to a String.
945    pub fn selection_to_string(&self) -> Option<String> {
946        let selection = self.grid.selection.clone()?;
947        let SelectionRange { start, end, is_block } = selection.to_range(self)?;
948
949        let mut res = String::new();
950
951        if is_block {
952            for line in (end.line + 1..=start.line).rev() {
953                res += &(self.line_to_string(line, start.col..end.col) + "\n");
954            }
955            res += &self.line_to_string(end.line, start.col..end.col);
956        } else {
957            res = self.bounds_to_string(start, end);
958        }
959
960        Some(res)
961    }
962
963    /// Convert range between two points to a String.
964    pub fn bounds_to_string(&self, start: Point<usize>, end: Point<usize>) -> String {
965        let mut res = String::new();
966
967        for line in (end.line..=start.line).rev() {
968            let start_col = if line == start.line { start.col } else { Column(0) };
969            let end_col = if line == end.line { end.col } else { self.cols() - 1 };
970
971            res += &self.line_to_string(line, start_col..end_col);
972        }
973
974        res
975    }
976
977    /// Convert a single line in the grid to a String.
978    fn line_to_string(&self, line: usize, cols: Range<Column>) -> String {
979        let mut text = String::new();
980
981        let grid_line = &self.grid[line];
982        let line_length = grid_line.line_length();
983        let line_end = min(line_length, cols.end + 1);
984
985        let mut tab_mode = false;
986
987        for col in IndexRange::from(cols.start..line_end) {
988            let cell = grid_line[col];
989
990            // Skip over cells until next tab-stop once a tab was found
991            if tab_mode {
992                if self.tabs[col] {
993                    tab_mode = false;
994                } else {
995                    continue;
996                }
997            }
998
999            if cell.c == '\t' {
1000                tab_mode = true;
1001            }
1002
1003            if !cell.flags.contains(Flags::WIDE_CHAR_SPACER) {
1004                // Push cells primary character
1005                text.push(cell.c);
1006
1007                // Push zero-width characters
1008                for c in (&cell.chars()[1..]).iter().take_while(|c| **c != ' ') {
1009                    text.push(*c);
1010                }
1011            }
1012        }
1013
1014        if cols.end >= self.cols() - 1
1015            && (line_end == Column(0)
1016                || !self.grid[line][line_end - 1].flags.contains(Flags::WRAPLINE))
1017        {
1018            text.push('\n');
1019        }
1020
1021        text
1022    }
1023
1024    pub fn visible_to_buffer(&self, point: Point) -> Point<usize> {
1025        self.grid.visible_to_buffer(point)
1026    }
1027
1028    pub fn buffer_to_visible(&self, point: impl Into<Point<usize>>) -> Option<Point<usize>> {
1029        self.grid.buffer_to_visible(point)
1030    }
1031
1032    /// Access to the raw grid data structure
1033    ///
1034    /// This is a bit of a hack; when the window is closed, the event processor
1035    /// serializes the grid state to a file.
1036    pub fn grid(&self) -> &Grid<Cell> {
1037        &self.grid
1038    }
1039
1040    /// Mutable access for swapping out the grid during tests
1041    #[cfg(test)]
1042    pub fn grid_mut(&mut self) -> &mut Grid<Cell> {
1043        &mut self.grid
1044    }
1045
1046    /// Iterate over the *renderable* cells in the terminal
1047    ///
1048    /// A renderable cell is any cell which has content other than the default
1049    /// background color.  Cells with an alternate background color are
1050    /// considered renderable as are cells with any text content.
1051    pub fn renderable_cells<'b, C>(&'b self, config: &'b Config<C>) -> RenderableCellsIter<'_, C> {
1052        let selection = self.grid.selection.as_ref().and_then(|s| s.to_range(self));
1053
1054        let cursor = if self.is_focused || !config.cursor.unfocused_hollow() {
1055            self.cursor_style.unwrap_or(self.default_cursor_style)
1056        } else {
1057            CursorStyle::HollowBlock
1058        };
1059
1060        RenderableCellsIter::new(&self, config, selection, cursor)
1061    }
1062
1063    /// Resize terminal to new dimensions
1064    pub fn resize(&mut self, size: &SizeInfo) {
1065        let old_cols = self.grid.num_cols();
1066        let old_lines = self.grid.num_lines();
1067        let mut num_cols = size.cols();
1068        let mut num_lines = size.lines();
1069
1070        if old_cols == num_cols && old_lines == num_lines {
1071            debug!("Term::resize dimensions unchanged");
1072            return;
1073        }
1074
1075        self.grid.selection = None;
1076        self.alt_grid.selection = None;
1077
1078        // Should not allow less than 1 col, causes all sorts of checks to be required.
1079        if num_cols <= Column(1) {
1080            num_cols = Column(2);
1081        }
1082
1083        // Should not allow less than 1 line, causes all sorts of checks to be required.
1084        if num_lines <= Line(1) {
1085            num_lines = Line(2);
1086        }
1087
1088        // Scroll up to keep cursor in terminal
1089        if self.cursor.point.line >= num_lines {
1090            let lines = self.cursor.point.line - num_lines + 1;
1091            let template = Cell { bg: self.cursor.template.bg, ..Cell::default() };
1092            self.grid.scroll_up(&(Line(0)..old_lines), lines, &template);
1093        }
1094
1095        // Scroll up alt grid as well
1096        if self.cursor_save_alt.point.line >= num_lines {
1097            let lines = self.cursor_save_alt.point.line - num_lines + 1;
1098            let template = Cell { bg: self.cursor_save_alt.template.bg, ..Cell::default() };
1099            self.alt_grid.scroll_up(&(Line(0)..old_lines), lines, &template);
1100        }
1101
1102        // Move prompt down when growing if scrollback lines are available
1103        if num_lines > old_lines && !self.mode.contains(TermMode::ALT_SCREEN) {
1104            let growage = min(num_lines - old_lines, Line(self.grid.history_size()));
1105            self.cursor.point.line += growage;
1106        }
1107
1108        debug!("New num_cols is {} and num_lines is {}", num_cols, num_lines);
1109
1110        // Resize grids to new size
1111        let is_alt = self.mode.contains(TermMode::ALT_SCREEN);
1112        let alt_cursor_point =
1113            if is_alt { &mut self.cursor_save.point } else { &mut self.cursor_save_alt.point };
1114        self.grid.resize(!is_alt, num_lines, num_cols, &mut self.cursor.point, &Cell::default());
1115        self.alt_grid.resize(is_alt, num_lines, num_cols, alt_cursor_point, &Cell::default());
1116
1117        // Reset scrolling region to new size
1118        self.scroll_region = Line(0)..self.grid.num_lines();
1119
1120        // Ensure cursors are in-bounds.
1121        self.cursor.point.col = min(self.cursor.point.col, num_cols - 1);
1122        self.cursor.point.line = min(self.cursor.point.line, num_lines - 1);
1123        self.cursor_save.point.col = min(self.cursor_save.point.col, num_cols - 1);
1124        self.cursor_save.point.line = min(self.cursor_save.point.line, num_lines - 1);
1125        self.cursor_save_alt.point.col = min(self.cursor_save_alt.point.col, num_cols - 1);
1126        self.cursor_save_alt.point.line = min(self.cursor_save_alt.point.line, num_lines - 1);
1127
1128        // Recreate tabs list
1129        self.tabs = TabStops::new(self.grid.num_cols(), self.tabspaces);
1130    }
1131
1132    #[inline]
1133    pub fn mode(&self) -> &TermMode {
1134        &self.mode
1135    }
1136
1137    #[inline]
1138    pub fn cursor(&self) -> &Cursor {
1139        &self.cursor
1140    }
1141
1142    pub fn swap_alt(&mut self) {
1143        if self.alt {
1144            let template = self.cursor.template;
1145            self.grid.region_mut(..).each(|c| c.reset(&template));
1146        }
1147
1148        self.alt = !self.alt;
1149        std::mem::swap(&mut self.grid, &mut self.alt_grid);
1150    }
1151
1152    /// Scroll screen down
1153    ///
1154    /// Text moves down; clear at bottom
1155    /// Expects origin to be in scroll range.
1156    #[inline]
1157    fn scroll_down_relative(&mut self, origin: Line, mut lines: Line) {
1158        trace!("Scrolling down relative: origin={}, lines={}", origin, lines);
1159        lines = min(lines, self.scroll_region.end - self.scroll_region.start);
1160        lines = min(lines, self.scroll_region.end - origin);
1161
1162        // Scroll between origin and bottom
1163        let template = Cell { bg: self.cursor.template.bg, ..Cell::default() };
1164        self.grid.scroll_down(&(origin..self.scroll_region.end), lines, &template);
1165    }
1166
1167    /// Scroll screen up
1168    ///
1169    /// Text moves up; clear at top
1170    /// Expects origin to be in scroll range.
1171    #[inline]
1172    fn scroll_up_relative(&mut self, origin: Line, lines: Line) {
1173        trace!("Scrolling up relative: origin={}, lines={}", origin, lines);
1174        let lines = min(lines, self.scroll_region.end - self.scroll_region.start);
1175
1176        // Scroll from origin to bottom less number of lines
1177        let template = Cell { bg: self.cursor.template.bg, ..Cell::default() };
1178        self.grid.scroll_up(&(origin..self.scroll_region.end), lines, &template);
1179    }
1180
1181    fn deccolm(&mut self)
1182    where
1183        T: EventListener,
1184    {
1185        // Setting 132 column font makes no sense, but run the other side effects
1186        // Clear scrolling region
1187        self.set_scrolling_region(1, self.grid.num_lines().0);
1188
1189        // Clear grid
1190        let template = self.cursor.template;
1191        self.grid.region_mut(..).each(|c| c.reset(&template));
1192    }
1193
1194    #[inline]
1195    pub fn background_color(&self) -> Rgb {
1196        self.colors[NamedColor::Background]
1197    }
1198
1199    #[inline]
1200    pub fn exit(&mut self)
1201    where
1202        T: EventListener,
1203    {
1204        self.event_proxy.send_event(Event::Exit);
1205    }
1206
1207    pub fn clipboard(&mut self) -> &mut Clipboard {
1208        &mut self.clipboard
1209    }
1210
1211    /// Insert a linebreak at the current cursor position.
1212    #[inline]
1213    fn wrapline(&mut self)
1214    where
1215        T: EventListener,
1216    {
1217        if !self.mode.contains(TermMode::LINE_WRAP) {
1218            return;
1219        }
1220
1221        trace!("Wrapping input");
1222
1223        self.grid[&self.cursor.point].flags.insert(Flags::WRAPLINE);
1224
1225        if (self.cursor.point.line + 1) >= self.scroll_region.end {
1226            self.linefeed();
1227        } else {
1228            self.cursor.point.line += 1;
1229        }
1230
1231        self.cursor.point.col = Column(0);
1232        self.input_needs_wrap = false;
1233    }
1234
1235    /// Write `c` to the cell at the cursor position.
1236    #[inline]
1237    fn write_at_cursor(&mut self, c: char) -> &mut Cell
1238    where
1239        T: EventListener,
1240    {
1241        let cell = &mut self.grid[&self.cursor.point];
1242        *cell = self.cursor.template;
1243        cell.c = self.cursor.charsets[self.active_charset].map(c);
1244        cell
1245    }
1246}
1247
1248impl<T> TermInfo for Term<T> {
1249    #[inline]
1250    fn lines(&self) -> Line {
1251        self.grid.num_lines()
1252    }
1253
1254    #[inline]
1255    fn cols(&self) -> Column {
1256        self.grid.num_cols()
1257    }
1258}
1259
1260impl<T: EventListener> Handler for Term<T> {
1261    #[inline]
1262    #[cfg(not(windows))]
1263    fn set_title(&mut self, title: &str) {
1264        if self.dynamic_title {
1265            trace!("Setting window title to '{}'", title);
1266
1267            self.title = title.into();
1268            self.event_proxy.send_event(Event::Title(title.to_owned()));
1269        }
1270    }
1271
1272    #[inline]
1273    #[cfg(windows)]
1274    fn set_title(&mut self, title: &str) {
1275        if self.dynamic_title {
1276            // cmd.exe in winpty: winpty incorrectly sets the title to ' ' instead of
1277            // 'Alacritty' - thus we have to substitute this back to get equivalent
1278            // behaviour as conpty.
1279            //
1280            // The starts_with check is necessary because other shells e.g. bash set a
1281            // different title and don't need Alacritty prepended.
1282            trace!("Setting window title to '{}'", title);
1283
1284            let title = if !tty::is_conpty() && title.starts_with(' ') {
1285                format!("Alacritty {}", title.trim())
1286            } else {
1287                title.to_owned()
1288            };
1289
1290            self.title = title.clone();
1291            self.event_proxy.send_event(Event::Title(title));
1292        }
1293    }
1294
1295    /// A character to be displayed
1296    #[inline]
1297    fn input(&mut self, c: char) {
1298        // Number of cells the char will occupy
1299        let width = match c.width() {
1300            Some(width) => width,
1301            None => return,
1302        };
1303
1304        // Handle zero-width characters
1305        if width == 0 {
1306            let mut col = self.cursor.point.col.0.saturating_sub(1);
1307            let line = self.cursor.point.line;
1308            if self.grid[line][Column(col)].flags.contains(Flags::WIDE_CHAR_SPACER) {
1309                col = col.saturating_sub(1);
1310            }
1311            self.grid[line][Column(col)].push_extra(c);
1312            return;
1313        }
1314
1315        // Move cursor to next line
1316        if self.input_needs_wrap {
1317            self.wrapline();
1318        }
1319
1320        let num_cols = self.grid.num_cols();
1321
1322        // If in insert mode, first shift cells to the right
1323        if self.mode.contains(TermMode::INSERT) && self.cursor.point.col + width < num_cols {
1324            let line = self.cursor.point.line;
1325            let col = self.cursor.point.col;
1326            let line = &mut self.grid[line];
1327
1328            let src = line[col..].as_ptr();
1329            let dst = line[(col + width)..].as_mut_ptr();
1330            unsafe {
1331                ptr::copy(src, dst, (num_cols - col - width).0);
1332            }
1333        }
1334
1335        if width == 1 {
1336            self.write_at_cursor(c);
1337        } else {
1338            // Insert extra placeholder before wide char if glyph doesn't fit in this row anymore
1339            if self.cursor.point.col + 1 >= num_cols {
1340                self.write_at_cursor(' ').flags.insert(Flags::WIDE_CHAR_SPACER);
1341                self.wrapline();
1342            }
1343
1344            // Write full width glyph to current cursor cell
1345            self.write_at_cursor(c).flags.insert(Flags::WIDE_CHAR);
1346
1347            // Write spacer to cell following the wide glyph
1348            self.cursor.point.col += 1;
1349            self.write_at_cursor(' ').flags.insert(Flags::WIDE_CHAR_SPACER);
1350        }
1351
1352        if self.cursor.point.col + 1 < num_cols {
1353            self.cursor.point.col += 1;
1354        } else {
1355            self.input_needs_wrap = true;
1356        }
1357    }
1358
1359    #[inline]
1360    fn decaln(&mut self) {
1361        trace!("Decalnning");
1362
1363        let template = Cell { c: 'E', ..Cell::default() };
1364        self.grid.region_mut(..).each(|c| c.reset(&template));
1365    }
1366
1367    #[inline]
1368    fn goto(&mut self, line: Line, col: Column) {
1369        trace!("Going to: line={}, col={}", line, col);
1370        let (y_offset, max_y) = if self.mode.contains(TermMode::ORIGIN) {
1371            (self.scroll_region.start, self.scroll_region.end - 1)
1372        } else {
1373            (Line(0), self.grid.num_lines() - 1)
1374        };
1375
1376        self.cursor.point.line = min(line + y_offset, max_y);
1377        self.cursor.point.col = min(col, self.grid.num_cols() - 1);
1378        self.input_needs_wrap = false;
1379    }
1380
1381    #[inline]
1382    fn goto_line(&mut self, line: Line) {
1383        trace!("Going to line: {}", line);
1384        self.goto(line, self.cursor.point.col)
1385    }
1386
1387    #[inline]
1388    fn goto_col(&mut self, col: Column) {
1389        trace!("Going to column: {}", col);
1390        self.goto(self.cursor.point.line, col)
1391    }
1392
1393    #[inline]
1394    fn insert_blank(&mut self, count: Column) {
1395        // Ensure inserting within terminal bounds
1396
1397        let count = min(count, self.grid.num_cols() - self.cursor.point.col);
1398
1399        let source = self.cursor.point.col;
1400        let destination = self.cursor.point.col + count;
1401        let num_cells = (self.grid.num_cols() - destination).0;
1402
1403        let line = &mut self.grid[self.cursor.point.line];
1404
1405        unsafe {
1406            let src = line[source..].as_ptr();
1407            let dst = line[destination..].as_mut_ptr();
1408
1409            ptr::copy(src, dst, num_cells);
1410        }
1411
1412        // Cells were just moved out towards the end of the line; fill in
1413        // between source and dest with blanks.
1414        for c in &mut line[source..destination] {
1415            c.reset(&self.cursor.template);
1416        }
1417    }
1418
1419    #[inline]
1420    fn move_up(&mut self, lines: Line) {
1421        trace!("Moving up: {}", lines);
1422        let move_to = Line(self.cursor.point.line.0.saturating_sub(lines.0));
1423        self.goto(move_to, self.cursor.point.col)
1424    }
1425
1426    #[inline]
1427    fn move_down(&mut self, lines: Line) {
1428        trace!("Moving down: {}", lines);
1429        let move_to = self.cursor.point.line + lines;
1430        self.goto(move_to, self.cursor.point.col)
1431    }
1432
1433    #[inline]
1434    fn move_forward(&mut self, cols: Column) {
1435        trace!("Moving forward: {}", cols);
1436        self.cursor.point.col = min(self.cursor.point.col + cols, self.grid.num_cols() - 1);
1437        self.input_needs_wrap = false;
1438    }
1439
1440    #[inline]
1441    fn move_backward(&mut self, cols: Column) {
1442        trace!("Moving backward: {}", cols);
1443        self.cursor.point.col -= min(self.cursor.point.col, cols);
1444        self.input_needs_wrap = false;
1445    }
1446
1447    #[inline]
1448    fn identify_terminal<W: io::Write>(&mut self, writer: &mut W) {
1449        trace!("Reporting terminal identity");
1450        let _ = writer.write_all(b"\x1b[?6c");
1451    }
1452
1453    #[inline]
1454    fn device_status<W: io::Write>(&mut self, writer: &mut W, arg: usize) {
1455        trace!("Reporting device status: {}", arg);
1456        match arg {
1457            5 => {
1458                let _ = writer.write_all(b"\x1b[0n");
1459            },
1460            6 => {
1461                let pos = self.cursor.point;
1462                let response = format!("\x1b[{};{}R", pos.line + 1, pos.col + 1);
1463                let _ = writer.write_all(response.as_bytes());
1464            },
1465            _ => debug!("unknown device status query: {}", arg),
1466        };
1467    }
1468
1469    #[inline]
1470    fn move_down_and_cr(&mut self, lines: Line) {
1471        trace!("Moving down and cr: {}", lines);
1472        let move_to = self.cursor.point.line + lines;
1473        self.goto(move_to, Column(0))
1474    }
1475
1476    #[inline]
1477    fn move_up_and_cr(&mut self, lines: Line) {
1478        trace!("Moving up and cr: {}", lines);
1479        let move_to = Line(self.cursor.point.line.0.saturating_sub(lines.0));
1480        self.goto(move_to, Column(0))
1481    }
1482
1483    #[inline]
1484    fn put_tab(&mut self, mut count: i64) {
1485        trace!("Putting tab: {}", count);
1486
1487        while self.cursor.point.col < self.grid.num_cols() && count != 0 {
1488            count -= 1;
1489
1490            let cell = &mut self.grid[&self.cursor.point];
1491            if cell.c == ' ' {
1492                cell.c = self.cursor.charsets[self.active_charset].map('\t');
1493            }
1494
1495            loop {
1496                if (self.cursor.point.col + 1) == self.grid.num_cols() {
1497                    break;
1498                }
1499
1500                self.cursor.point.col += 1;
1501
1502                if self.tabs[self.cursor.point.col] {
1503                    break;
1504                }
1505            }
1506        }
1507
1508        self.input_needs_wrap = false;
1509    }
1510
1511    /// Backspace `count` characters
1512    #[inline]
1513    fn backspace(&mut self) {
1514        trace!("Backspace");
1515        if self.cursor.point.col > Column(0) {
1516            self.cursor.point.col -= 1;
1517            self.input_needs_wrap = false;
1518        }
1519    }
1520
1521    /// Carriage return
1522    #[inline]
1523    fn carriage_return(&mut self) {
1524        trace!("Carriage return");
1525        self.cursor.point.col = Column(0);
1526        self.input_needs_wrap = false;
1527    }
1528
1529    /// Linefeed
1530    #[inline]
1531    fn linefeed(&mut self) {
1532        trace!("Linefeed");
1533        let next = self.cursor.point.line + 1;
1534        if next == self.scroll_region.end {
1535            self.scroll_up(Line(1));
1536        } else if next < self.grid.num_lines() {
1537            self.cursor.point.line += 1;
1538        }
1539    }
1540
1541    /// Set current position as a tabstop
1542    #[inline]
1543    fn bell(&mut self) {
1544        trace!("Bell");
1545        self.visual_bell.ring();
1546        self.event_proxy.send_event(Event::Urgent);
1547    }
1548
1549    #[inline]
1550    fn substitute(&mut self) {
1551        trace!("[unimplemented] Substitute");
1552    }
1553
1554    /// Run LF/NL
1555    ///
1556    /// LF/NL mode has some interesting history. According to ECMA-48 4th
1557    /// edition, in LINE FEED mode,
1558    ///
1559    /// > The execution of the formatter functions LINE FEED (LF), FORM FEED
1560    /// (FF), LINE TABULATION (VT) cause only movement of the active position in
1561    /// the direction of the line progression.
1562    ///
1563    /// In NEW LINE mode,
1564    ///
1565    /// > The execution of the formatter functions LINE FEED (LF), FORM FEED
1566    /// (FF), LINE TABULATION (VT) cause movement to the line home position on
1567    /// the following line, the following form, etc. In the case of LF this is
1568    /// referred to as the New Line (NL) option.
1569    ///
1570    /// Additionally, ECMA-48 4th edition says that this option is deprecated.
1571    /// ECMA-48 5th edition only mentions this option (without explanation)
1572    /// saying that it's been removed.
1573    ///
1574    /// As an emulator, we need to support it since applications may still rely
1575    /// on it.
1576    #[inline]
1577    fn newline(&mut self) {
1578        self.linefeed();
1579
1580        if self.mode.contains(TermMode::LINE_FEED_NEW_LINE) {
1581            self.carriage_return();
1582        }
1583    }
1584
1585    #[inline]
1586    fn set_horizontal_tabstop(&mut self) {
1587        trace!("Setting horizontal tabstop");
1588        let column = self.cursor.point.col;
1589        self.tabs[column] = true;
1590    }
1591
1592    #[inline]
1593    fn scroll_up(&mut self, lines: Line) {
1594        let origin = self.scroll_region.start;
1595        self.scroll_up_relative(origin, lines);
1596    }
1597
1598    #[inline]
1599    fn scroll_down(&mut self, lines: Line) {
1600        let origin = self.scroll_region.start;
1601        self.scroll_down_relative(origin, lines);
1602    }
1603
1604    #[inline]
1605    fn insert_blank_lines(&mut self, lines: Line) {
1606        trace!("Inserting blank {} lines", lines);
1607        if self.scroll_region.contains(&self.cursor.point.line) {
1608            let origin = self.cursor.point.line;
1609            self.scroll_down_relative(origin, lines);
1610        }
1611    }
1612
1613    #[inline]
1614    fn delete_lines(&mut self, lines: Line) {
1615        let origin = self.cursor.point.line;
1616        let lines = min(self.lines() - origin, lines);
1617
1618        trace!("Deleting {} lines", lines);
1619
1620        if lines.0 > 0 && self.scroll_region.contains(&self.cursor.point.line) {
1621            self.scroll_up_relative(origin, lines);
1622        }
1623    }
1624
1625    #[inline]
1626    fn erase_chars(&mut self, count: Column) {
1627        trace!("Erasing chars: count={}, col={}", count, self.cursor.point.col);
1628        let start = self.cursor.point.col;
1629        let end = min(start + count, self.grid.num_cols());
1630
1631        let row = &mut self.grid[self.cursor.point.line];
1632        // Cleared cells have current background color set
1633        for c in &mut row[start..end] {
1634            c.reset(&self.cursor.template);
1635        }
1636    }
1637
1638    #[inline]
1639    fn delete_chars(&mut self, count: Column) {
1640        let cols = self.grid.num_cols();
1641
1642        // Ensure deleting within terminal bounds
1643        let count = min(count, cols);
1644
1645        let start = self.cursor.point.col;
1646        let end = min(start + count, cols - 1);
1647        let n = (cols - end).0;
1648
1649        let line = &mut self.grid[self.cursor.point.line];
1650
1651        unsafe {
1652            let src = line[end..].as_ptr();
1653            let dst = line[start..].as_mut_ptr();
1654
1655            ptr::copy(src, dst, n);
1656        }
1657
1658        // Clear last `count` cells in line. If deleting 1 char, need to delete
1659        // 1 cell.
1660        let end = cols - count;
1661        for c in &mut line[end..] {
1662            c.reset(&self.cursor.template);
1663        }
1664    }
1665
1666    #[inline]
1667    fn move_backward_tabs(&mut self, count: i64) {
1668        trace!("Moving backward {} tabs", count);
1669
1670        for _ in 0..count {
1671            let mut col = self.cursor.point.col;
1672            for i in (0..(col.0)).rev() {
1673                if self.tabs[index::Column(i)] {
1674                    col = index::Column(i);
1675                    break;
1676                }
1677            }
1678            self.cursor.point.col = col;
1679        }
1680    }
1681
1682    #[inline]
1683    fn move_forward_tabs(&mut self, count: i64) {
1684        trace!("[unimplemented] Moving forward {} tabs", count);
1685    }
1686
1687    #[inline]
1688    fn save_cursor_position(&mut self) {
1689        trace!("Saving cursor position");
1690        let cursor = if self.alt { &mut self.cursor_save_alt } else { &mut self.cursor_save };
1691
1692        *cursor = self.cursor;
1693    }
1694
1695    #[inline]
1696    fn restore_cursor_position(&mut self) {
1697        trace!("Restoring cursor position");
1698        let source = if self.alt { &self.cursor_save_alt } else { &self.cursor_save };
1699
1700        self.cursor = *source;
1701        self.cursor.point.line = min(self.cursor.point.line, self.grid.num_lines() - 1);
1702        self.cursor.point.col = min(self.cursor.point.col, self.grid.num_cols() - 1);
1703    }
1704
1705    #[inline]
1706    fn clear_line(&mut self, mode: ansi::LineClearMode) {
1707        trace!("Clearing line: {:?}", mode);
1708
1709        let col = self.cursor.point.col;
1710
1711        match mode {
1712            ansi::LineClearMode::Right => {
1713                let row = &mut self.grid[self.cursor.point.line];
1714                for cell in &mut row[col..] {
1715                    cell.reset(&self.cursor.template);
1716                }
1717            },
1718            ansi::LineClearMode::Left => {
1719                let row = &mut self.grid[self.cursor.point.line];
1720                for cell in &mut row[..=col] {
1721                    cell.reset(&self.cursor.template);
1722                }
1723            },
1724            ansi::LineClearMode::All => {
1725                let row = &mut self.grid[self.cursor.point.line];
1726                for cell in &mut row[..] {
1727                    cell.reset(&self.cursor.template);
1728                }
1729            },
1730        }
1731    }
1732
1733    /// Set the indexed color value
1734    #[inline]
1735    fn set_color(&mut self, index: usize, color: Rgb) {
1736        trace!("Setting color[{}] = {:?}", index, color);
1737        self.colors[index] = color;
1738        self.color_modified[index] = true;
1739    }
1740
1741    /// Write a foreground/background color escape sequence with the current color
1742    #[inline]
1743    fn dynamic_color_sequence<W: io::Write>(&mut self, writer: &mut W, code: u8, index: usize) {
1744        trace!("Writing escape sequence for dynamic color code {}: color[{}]", code, index);
1745        let color = self.colors[index];
1746        let response = format!(
1747            "\x1b]{};rgb:{1:02x}{1:02x}/{2:02x}{2:02x}/{3:02x}{3:02x}\x07",
1748            code, color.r, color.g, color.b
1749        );
1750        let _ = writer.write_all(response.as_bytes());
1751    }
1752
1753    /// Reset the indexed color to original value
1754    #[inline]
1755    fn reset_color(&mut self, index: usize) {
1756        trace!("Resetting color[{}]", index);
1757        self.colors[index] = self.original_colors[index];
1758        self.color_modified[index] = false;
1759    }
1760
1761    /// Set the clipboard
1762    #[inline]
1763    fn set_clipboard(&mut self, clipboard: u8, base64: &[u8]) {
1764        let clipboard_type = match clipboard {
1765            b'c' => ClipboardType::Clipboard,
1766            b'p' | b's' => ClipboardType::Selection,
1767            _ => return,
1768        };
1769
1770        if let Ok(bytes) = base64::decode(base64) {
1771            if let Ok(text) = str::from_utf8(&bytes) {
1772                self.clipboard.store(clipboard_type, text);
1773            }
1774        }
1775    }
1776
1777    /// Write clipboard data to child.
1778    #[inline]
1779    fn write_clipboard<W: io::Write>(&mut self, clipboard: u8, writer: &mut W) {
1780        let clipboard_type = match clipboard {
1781            b'c' => ClipboardType::Clipboard,
1782            b'p' | b's' => ClipboardType::Selection,
1783            _ => return,
1784        };
1785
1786        let text = self.clipboard.load(clipboard_type);
1787        let base64 = base64::encode(&text);
1788        let escape = format!("\x1b]52;{};{}\x07", clipboard as char, base64);
1789        let _ = writer.write_all(escape.as_bytes());
1790    }
1791
1792    #[inline]
1793    fn clear_screen(&mut self, mode: ansi::ClearMode) {
1794        trace!("Clearing screen: {:?}", mode);
1795        let template = self.cursor.template;
1796
1797        // Remove active selections
1798        self.grid.selection = None;
1799
1800        match mode {
1801            ansi::ClearMode::Above => {
1802                // If clearing more than one line
1803                if self.cursor.point.line > Line(1) {
1804                    // Fully clear all lines before the current line
1805                    self.grid
1806                        .region_mut(..self.cursor.point.line)
1807                        .each(|cell| cell.reset(&template));
1808                }
1809                // Clear up to the current column in the current line
1810                let end = min(self.cursor.point.col + 1, self.grid.num_cols());
1811                for cell in &mut self.grid[self.cursor.point.line][..end] {
1812                    cell.reset(&template);
1813                }
1814            },
1815            ansi::ClearMode::Below => {
1816                for cell in &mut self.grid[self.cursor.point.line][self.cursor.point.col..] {
1817                    cell.reset(&template);
1818                }
1819                if self.cursor.point.line < self.grid.num_lines() - 1 {
1820                    self.grid
1821                        .region_mut((self.cursor.point.line + 1)..)
1822                        .each(|cell| cell.reset(&template));
1823                }
1824            },
1825            ansi::ClearMode::All => {
1826                if self.mode.contains(TermMode::ALT_SCREEN) {
1827                    self.grid.region_mut(..).each(|c| c.reset(&template));
1828                } else {
1829                    let template = Cell { bg: template.bg, ..Cell::default() };
1830                    self.grid.clear_viewport(&template);
1831                }
1832            },
1833            ansi::ClearMode::Saved => self.grid.clear_history(),
1834        }
1835    }
1836
1837    #[inline]
1838    fn clear_tabs(&mut self, mode: ansi::TabulationClearMode) {
1839        trace!("Clearing tabs: {:?}", mode);
1840        match mode {
1841            ansi::TabulationClearMode::Current => {
1842                let column = self.cursor.point.col;
1843                self.tabs[column] = false;
1844            },
1845            ansi::TabulationClearMode::All => {
1846                self.tabs.clear_all();
1847            },
1848        }
1849    }
1850
1851    // Reset all important fields in the term struct
1852    #[inline]
1853    fn reset_state(&mut self) {
1854        if self.alt {
1855            self.swap_alt();
1856        }
1857        self.input_needs_wrap = false;
1858        self.cursor = Default::default();
1859        self.active_charset = Default::default();
1860        self.mode = Default::default();
1861        self.cursor_save = Default::default();
1862        self.cursor_save_alt = Default::default();
1863        self.colors = self.original_colors;
1864        self.color_modified = [false; color::COUNT];
1865        self.cursor_style = None;
1866        self.grid.reset(&Cell::default());
1867        self.alt_grid.reset(&Cell::default());
1868        self.scroll_region = Line(0)..self.grid.num_lines();
1869        self.title = DEFAULT_NAME.to_string();
1870        self.title_stack.clear();
1871    }
1872
1873    #[inline]
1874    fn reverse_index(&mut self) {
1875        trace!("Reversing index");
1876        // if cursor is at the top
1877        if self.cursor.point.line == self.scroll_region.start {
1878            self.scroll_down(Line(1));
1879        } else {
1880            self.cursor.point.line -= min(self.cursor.point.line, Line(1));
1881        }
1882    }
1883
1884    /// set a terminal attribute
1885    #[inline]
1886    fn terminal_attribute(&mut self, attr: Attr) {
1887        trace!("Setting attribute: {:?}", attr);
1888        match attr {
1889            Attr::Foreground(color) => self.cursor.template.fg = color,
1890            Attr::Background(color) => self.cursor.template.bg = color,
1891            Attr::Reset => {
1892                self.cursor.template.fg = Color::Named(NamedColor::Foreground);
1893                self.cursor.template.bg = Color::Named(NamedColor::Background);
1894                self.cursor.template.flags = Flags::empty();
1895            },
1896            Attr::Reverse => self.cursor.template.flags.insert(Flags::INVERSE),
1897            Attr::CancelReverse => self.cursor.template.flags.remove(Flags::INVERSE),
1898            Attr::Bold => self.cursor.template.flags.insert(Flags::BOLD),
1899            Attr::CancelBold => self.cursor.template.flags.remove(Flags::BOLD),
1900            Attr::Dim => self.cursor.template.flags.insert(Flags::DIM),
1901            Attr::CancelBoldDim => self.cursor.template.flags.remove(Flags::BOLD | Flags::DIM),
1902            Attr::Italic => self.cursor.template.flags.insert(Flags::ITALIC),
1903            Attr::CancelItalic => self.cursor.template.flags.remove(Flags::ITALIC),
1904            Attr::Underline => self.cursor.template.flags.insert(Flags::UNDERLINE),
1905            Attr::CancelUnderline => self.cursor.template.flags.remove(Flags::UNDERLINE),
1906            Attr::Hidden => self.cursor.template.flags.insert(Flags::HIDDEN),
1907            Attr::CancelHidden => self.cursor.template.flags.remove(Flags::HIDDEN),
1908            Attr::Strike => self.cursor.template.flags.insert(Flags::STRIKEOUT),
1909            Attr::CancelStrike => self.cursor.template.flags.remove(Flags::STRIKEOUT),
1910            _ => {
1911                debug!("Term got unhandled attr: {:?}", attr);
1912            },
1913        }
1914    }
1915
1916    #[inline]
1917    fn set_mode(&mut self, mode: ansi::Mode) {
1918        trace!("Setting mode: {:?}", mode);
1919        match mode {
1920            ansi::Mode::SwapScreenAndSetRestoreCursor => {
1921                if !self.alt {
1922                    self.mode.insert(TermMode::ALT_SCREEN);
1923                    self.save_cursor_position();
1924                    self.swap_alt();
1925                    self.save_cursor_position();
1926                }
1927            },
1928            ansi::Mode::ShowCursor => self.mode.insert(TermMode::SHOW_CURSOR),
1929            ansi::Mode::CursorKeys => self.mode.insert(TermMode::APP_CURSOR),
1930            // Mouse protocols are mutually exlusive
1931            ansi::Mode::ReportMouseClicks => {
1932                self.mode.remove(TermMode::MOUSE_MODE);
1933                self.mode.insert(TermMode::MOUSE_REPORT_CLICK);
1934                self.event_proxy.send_event(Event::MouseCursorDirty);
1935            },
1936            ansi::Mode::ReportCellMouseMotion => {
1937                self.mode.remove(TermMode::MOUSE_MODE);
1938                self.mode.insert(TermMode::MOUSE_DRAG);
1939                self.event_proxy.send_event(Event::MouseCursorDirty);
1940            },
1941            ansi::Mode::ReportAllMouseMotion => {
1942                self.mode.remove(TermMode::MOUSE_MODE);
1943                self.mode.insert(TermMode::MOUSE_MOTION);
1944                self.event_proxy.send_event(Event::MouseCursorDirty);
1945            },
1946            ansi::Mode::ReportFocusInOut => self.mode.insert(TermMode::FOCUS_IN_OUT),
1947            ansi::Mode::BracketedPaste => self.mode.insert(TermMode::BRACKETED_PASTE),
1948            // Mouse encodings are mutually exlusive
1949            ansi::Mode::SgrMouse => {
1950                self.mode.remove(TermMode::UTF8_MOUSE);
1951                self.mode.insert(TermMode::SGR_MOUSE);
1952            },
1953            ansi::Mode::Utf8Mouse => {
1954                self.mode.remove(TermMode::SGR_MOUSE);
1955                self.mode.insert(TermMode::UTF8_MOUSE);
1956            },
1957            ansi::Mode::AlternateScroll => self.mode.insert(TermMode::ALTERNATE_SCROLL),
1958            ansi::Mode::LineWrap => self.mode.insert(TermMode::LINE_WRAP),
1959            ansi::Mode::LineFeedNewLine => self.mode.insert(TermMode::LINE_FEED_NEW_LINE),
1960            ansi::Mode::Origin => self.mode.insert(TermMode::ORIGIN),
1961            ansi::Mode::DECCOLM => self.deccolm(),
1962            ansi::Mode::Insert => self.mode.insert(TermMode::INSERT), // heh
1963            ansi::Mode::BlinkingCursor => {
1964                trace!("... unimplemented mode");
1965            },
1966        }
1967    }
1968
1969    #[inline]
1970    fn unset_mode(&mut self, mode: ansi::Mode) {
1971        trace!("Unsetting mode: {:?}", mode);
1972        match mode {
1973            ansi::Mode::SwapScreenAndSetRestoreCursor => {
1974                if self.alt {
1975                    self.mode.remove(TermMode::ALT_SCREEN);
1976                    self.restore_cursor_position();
1977                    self.swap_alt();
1978                    self.restore_cursor_position();
1979                }
1980            },
1981            ansi::Mode::ShowCursor => self.mode.remove(TermMode::SHOW_CURSOR),
1982            ansi::Mode::CursorKeys => self.mode.remove(TermMode::APP_CURSOR),
1983            ansi::Mode::ReportMouseClicks => {
1984                self.mode.remove(TermMode::MOUSE_REPORT_CLICK);
1985                self.event_proxy.send_event(Event::MouseCursorDirty);
1986            },
1987            ansi::Mode::ReportCellMouseMotion => {
1988                self.mode.remove(TermMode::MOUSE_DRAG);
1989                self.event_proxy.send_event(Event::MouseCursorDirty);
1990            },
1991            ansi::Mode::ReportAllMouseMotion => {
1992                self.mode.remove(TermMode::MOUSE_MOTION);
1993                self.event_proxy.send_event(Event::MouseCursorDirty);
1994            },
1995            ansi::Mode::ReportFocusInOut => self.mode.remove(TermMode::FOCUS_IN_OUT),
1996            ansi::Mode::BracketedPaste => self.mode.remove(TermMode::BRACKETED_PASTE),
1997            ansi::Mode::SgrMouse => self.mode.remove(TermMode::SGR_MOUSE),
1998            ansi::Mode::Utf8Mouse => self.mode.remove(TermMode::UTF8_MOUSE),
1999            ansi::Mode::AlternateScroll => self.mode.remove(TermMode::ALTERNATE_SCROLL),
2000            ansi::Mode::LineWrap => self.mode.remove(TermMode::LINE_WRAP),
2001            ansi::Mode::LineFeedNewLine => self.mode.remove(TermMode::LINE_FEED_NEW_LINE),
2002            ansi::Mode::Origin => self.mode.remove(TermMode::ORIGIN),
2003            ansi::Mode::DECCOLM => self.deccolm(),
2004            ansi::Mode::Insert => self.mode.remove(TermMode::INSERT),
2005            ansi::Mode::BlinkingCursor => {
2006                trace!("... unimplemented mode");
2007            },
2008        }
2009    }
2010
2011    #[inline]
2012    fn set_scrolling_region(&mut self, top: usize, bottom: usize) {
2013        if top >= bottom {
2014            debug!("Invalid scrolling region: ({};{})", top, bottom);
2015            return;
2016        }
2017
2018        // Bottom should be included in the range, but range end is not
2019        // usually included. One option would be to use an inclusive
2020        // range, but instead we just let the open range end be 1
2021        // higher.
2022        let start = Line(top - 1);
2023        let end = Line(bottom);
2024
2025        trace!("Setting scrolling region: ({};{})", start, end);
2026
2027        self.scroll_region.start = min(start, self.grid.num_lines());
2028        self.scroll_region.end = min(end, self.grid.num_lines());
2029        self.goto(Line(0), Column(0));
2030    }
2031
2032    #[inline]
2033    fn set_keypad_application_mode(&mut self) {
2034        trace!("Setting keypad application mode");
2035        self.mode.insert(TermMode::APP_KEYPAD);
2036    }
2037
2038    #[inline]
2039    fn unset_keypad_application_mode(&mut self) {
2040        trace!("Unsetting keypad application mode");
2041        self.mode.remove(TermMode::APP_KEYPAD);
2042    }
2043
2044    #[inline]
2045    fn configure_charset(&mut self, index: CharsetIndex, charset: StandardCharset) {
2046        trace!("Configuring charset {:?} as {:?}", index, charset);
2047        self.cursor.charsets[index] = charset;
2048    }
2049
2050    #[inline]
2051    fn set_active_charset(&mut self, index: CharsetIndex) {
2052        trace!("Setting active charset {:?}", index);
2053        self.active_charset = index;
2054    }
2055
2056    #[inline]
2057    fn set_cursor_style(&mut self, style: Option<CursorStyle>) {
2058        trace!("Setting cursor style {:?}", style);
2059        self.cursor_style = style;
2060    }
2061
2062    #[inline]
2063    fn push_title(&mut self) {
2064        trace!("Pushing '{}' onto title stack", self.title);
2065
2066        if self.title_stack.len() >= TITLE_STACK_MAX_DEPTH {
2067            let removed = self.title_stack.remove(0);
2068            trace!(
2069                "Removing '{}' from bottom of title stack that exceeds its maximum depth",
2070                removed
2071            );
2072        }
2073
2074        self.title_stack.push(self.title.clone());
2075    }
2076
2077    #[inline]
2078    fn pop_title(&mut self) {
2079        trace!("Attempting to pop title from stack...");
2080
2081        if let Some(popped) = self.title_stack.pop() {
2082            trace!("Title '{}' popped from stack", popped);
2083            self.set_title(&popped);
2084        }
2085    }
2086}
2087
2088struct TabStops {
2089    tabs: Vec<bool>,
2090}
2091
2092impl TabStops {
2093    fn new(num_cols: Column, tabspaces: usize) -> TabStops {
2094        TabStops {
2095            tabs: IndexRange::from(Column(0)..num_cols)
2096                .map(|i| (*i as usize) % tabspaces == 0)
2097                .collect::<Vec<bool>>(),
2098        }
2099    }
2100
2101    fn clear_all(&mut self) {
2102        unsafe {
2103            ptr::write_bytes(self.tabs.as_mut_ptr(), 0, self.tabs.len());
2104        }
2105    }
2106}
2107
2108impl Index<Column> for TabStops {
2109    type Output = bool;
2110
2111    fn index(&self, index: Column) -> &bool {
2112        &self.tabs[index.0]
2113    }
2114}
2115
2116impl IndexMut<Column> for TabStops {
2117    fn index_mut(&mut self, index: Column) -> &mut bool {
2118        self.tabs.index_mut(index.0)
2119    }
2120}
2121
2122#[cfg(test)]
2123mod tests {
2124    use std::mem;
2125
2126    use crate::ansi::{self, CharsetIndex, Handler, StandardCharset};
2127    use crate::clipboard::Clipboard;
2128    use crate::config::MockConfig;
2129    use crate::event::{Event, EventListener};
2130    use crate::grid::{Grid, Scroll};
2131    use crate::index::{Column, Line, Point, Side};
2132    use crate::selection::Selection;
2133    use crate::term::cell::{Cell, Flags};
2134    use crate::term::{SizeInfo, Term};
2135
2136    struct Mock;
2137    impl EventListener for Mock {
2138        fn send_event(&self, _event: Event) {}
2139    }
2140
2141    #[test]
2142    fn semantic_selection_works() {
2143        let size = SizeInfo {
2144            width: 21.0,
2145            height: 51.0,
2146            cell_width: 3.0,
2147            cell_height: 3.0,
2148            padding_x: 0.0,
2149            padding_y: 0.0,
2150            dpr: 1.0,
2151        };
2152        let mut term = Term::new(&MockConfig::default(), &size, Clipboard::new_nop(), Mock);
2153        let mut grid: Grid<Cell> = Grid::new(Line(3), Column(5), 0, Cell::default());
2154        for i in 0..5 {
2155            for j in 0..2 {
2156                grid[Line(j)][Column(i)].c = 'a';
2157            }
2158        }
2159        grid[Line(0)][Column(0)].c = '"';
2160        grid[Line(0)][Column(3)].c = '"';
2161        grid[Line(1)][Column(2)].c = '"';
2162        grid[Line(0)][Column(4)].flags.insert(Flags::WRAPLINE);
2163
2164        let mut escape_chars = String::from("\"");
2165
2166        mem::swap(&mut term.grid, &mut grid);
2167        mem::swap(&mut term.semantic_escape_chars, &mut escape_chars);
2168
2169        {
2170            *term.selection_mut() = Some(Selection::semantic(Point { line: 2, col: Column(1) }));
2171            assert_eq!(term.selection_to_string(), Some(String::from("aa")));
2172        }
2173
2174        {
2175            *term.selection_mut() = Some(Selection::semantic(Point { line: 2, col: Column(4) }));
2176            assert_eq!(term.selection_to_string(), Some(String::from("aaa")));
2177        }
2178
2179        {
2180            *term.selection_mut() = Some(Selection::semantic(Point { line: 1, col: Column(1) }));
2181            assert_eq!(term.selection_to_string(), Some(String::from("aaa")));
2182        }
2183    }
2184
2185    #[test]
2186    fn line_selection_works() {
2187        let size = SizeInfo {
2188            width: 21.0,
2189            height: 51.0,
2190            cell_width: 3.0,
2191            cell_height: 3.0,
2192            padding_x: 0.0,
2193            padding_y: 0.0,
2194            dpr: 1.0,
2195        };
2196        let mut term = Term::new(&MockConfig::default(), &size, Clipboard::new_nop(), Mock);
2197        let mut grid: Grid<Cell> = Grid::new(Line(1), Column(5), 0, Cell::default());
2198        for i in 0..5 {
2199            grid[Line(0)][Column(i)].c = 'a';
2200        }
2201        grid[Line(0)][Column(0)].c = '"';
2202        grid[Line(0)][Column(3)].c = '"';
2203
2204        mem::swap(&mut term.grid, &mut grid);
2205
2206        *term.selection_mut() = Some(Selection::lines(Point { line: 0, col: Column(3) }));
2207        assert_eq!(term.selection_to_string(), Some(String::from("\"aa\"a\n")));
2208    }
2209
2210    #[test]
2211    fn selecting_empty_line() {
2212        let size = SizeInfo {
2213            width: 21.0,
2214            height: 51.0,
2215            cell_width: 3.0,
2216            cell_height: 3.0,
2217            padding_x: 0.0,
2218            padding_y: 0.0,
2219            dpr: 1.0,
2220        };
2221        let mut term = Term::new(&MockConfig::default(), &size, Clipboard::new_nop(), Mock);
2222        let mut grid: Grid<Cell> = Grid::new(Line(3), Column(3), 0, Cell::default());
2223        for l in 0..3 {
2224            if l != 1 {
2225                for c in 0..3 {
2226                    grid[Line(l)][Column(c)].c = 'a';
2227                }
2228            }
2229        }
2230
2231        mem::swap(&mut term.grid, &mut grid);
2232
2233        let mut selection = Selection::simple(Point { line: 2, col: Column(0) }, Side::Left);
2234        selection.update(Point { line: 0, col: Column(2) }, Side::Right);
2235        *term.selection_mut() = Some(selection);
2236        assert_eq!(term.selection_to_string(), Some("aaa\n\naaa\n".into()));
2237    }
2238
2239    /// Check that the grid can be serialized back and forth losslessly
2240    ///
2241    /// This test is in the term module as opposed to the grid since we want to
2242    /// test this property with a T=Cell.
2243    #[test]
2244    fn grid_serde() {
2245        let template = Cell::default();
2246
2247        let grid: Grid<Cell> = Grid::new(Line(24), Column(80), 0, template);
2248        let serialized = serde_json::to_string(&grid).expect("ser");
2249        let deserialized = serde_json::from_str::<Grid<Cell>>(&serialized).expect("de");
2250
2251        assert_eq!(deserialized, grid);
2252    }
2253
2254    #[test]
2255    fn input_line_drawing_character() {
2256        let size = SizeInfo {
2257            width: 21.0,
2258            height: 51.0,
2259            cell_width: 3.0,
2260            cell_height: 3.0,
2261            padding_x: 0.0,
2262            padding_y: 0.0,
2263            dpr: 1.0,
2264        };
2265        let mut term = Term::new(&MockConfig::default(), &size, Clipboard::new_nop(), Mock);
2266        let cursor = Point::new(Line(0), Column(0));
2267        term.configure_charset(CharsetIndex::G0, StandardCharset::SpecialCharacterAndLineDrawing);
2268        term.input('a');
2269
2270        assert_eq!(term.grid()[&cursor].c, '▒');
2271    }
2272
2273    #[test]
2274    fn clear_saved_lines() {
2275        let size = SizeInfo {
2276            width: 21.0,
2277            height: 51.0,
2278            cell_width: 3.0,
2279            cell_height: 3.0,
2280            padding_x: 0.0,
2281            padding_y: 0.0,
2282            dpr: 1.0,
2283        };
2284        let mut term = Term::new(&MockConfig::default(), &size, Clipboard::new_nop(), Mock);
2285
2286        // Add one line of scrollback
2287        term.grid.scroll_up(&(Line(0)..Line(1)), Line(1), &Cell::default());
2288
2289        // Clear the history
2290        term.clear_screen(ansi::ClearMode::Saved);
2291
2292        // Make sure that scrolling does not change the grid
2293        let mut scrolled_grid = term.grid.clone();
2294        scrolled_grid.scroll_display(Scroll::Top);
2295
2296        // Truncate grids for comparison
2297        scrolled_grid.truncate();
2298        term.grid.truncate();
2299
2300        assert_eq!(term.grid, scrolled_grid);
2301    }
2302
2303    #[test]
2304    fn window_title() {
2305        let size = SizeInfo {
2306            width: 21.0,
2307            height: 51.0,
2308            cell_width: 3.0,
2309            cell_height: 3.0,
2310            padding_x: 0.0,
2311            padding_y: 0.0,
2312            dpr: 1.0,
2313        };
2314        let mut term = Term::new(&MockConfig::default(), &size, Clipboard::new_nop(), Mock);
2315
2316        // Title can be set
2317        {
2318            term.title = "Test".to_string();
2319            assert_eq!(term.title, "Test");
2320        }
2321
2322        // Title can be pushed onto stack
2323        {
2324            term.push_title();
2325            term.title = "Next".to_string();
2326            assert_eq!(term.title, "Next");
2327            assert_eq!(term.title_stack.get(0).unwrap(), "Test");
2328        }
2329
2330        // Title can be popped from stack and set as the window title
2331        {
2332            term.pop_title();
2333            assert_eq!(term.title, "Test");
2334            assert!(term.title_stack.is_empty());
2335        }
2336
2337        // Title stack doesn't grow infinitely
2338        {
2339            for _ in 0..4097 {
2340                term.push_title();
2341            }
2342            assert_eq!(term.title_stack.len(), 4096);
2343        }
2344
2345        // Title and title stack reset when terminal state is reset
2346        {
2347            term.push_title();
2348            term.reset_state();
2349            assert_eq!(term.title, "Alacritty");
2350            assert!(term.title_stack.is_empty());
2351        }
2352    }
2353}
2354
2355#[cfg(all(test, feature = "bench"))]
2356mod benches {
2357    extern crate serde_json as json;
2358    extern crate test;
2359
2360    use std::fs::File;
2361    use std::io::Read;
2362    use std::mem;
2363    use std::path::Path;
2364
2365    use crate::clipboard::Clipboard;
2366    use crate::config::MockConfig;
2367    use crate::event::{Event, EventListener};
2368    use crate::grid::Grid;
2369
2370    use super::cell::Cell;
2371    use super::{SizeInfo, Term};
2372
2373    struct Mock;
2374    impl EventListener for Mock {
2375        fn send_event(&self, _event: Event) {}
2376    }
2377
2378    fn read_string<P>(path: P) -> String
2379    where
2380        P: AsRef<Path>,
2381    {
2382        let mut res = String::new();
2383        File::open(path.as_ref()).unwrap().read_to_string(&mut res).unwrap();
2384
2385        res
2386    }
2387
2388    /// Benchmark for the renderable cells iterator
2389    ///
2390    /// The renderable cells iterator yields cells that require work to be
2391    /// displayed (that is, not a an empty background cell). This benchmark
2392    /// measures how long it takes to process the whole iterator.
2393    ///
2394    /// When this benchmark was first added, it averaged ~78usec on my macbook
2395    /// pro. The total render time for this grid is anywhere between ~1500 and
2396    /// ~2000usec (measured imprecisely with the visual meter).
2397    #[bench]
2398    fn render_iter(b: &mut test::Bencher) {
2399        // Need some realistic grid state; using one of the ref files.
2400        let serialized_grid = read_string(concat!(
2401            env!("CARGO_MANIFEST_DIR"),
2402            "/tests/ref/vim_large_window_scroll/grid.json"
2403        ));
2404        let serialized_size = read_string(concat!(
2405            env!("CARGO_MANIFEST_DIR"),
2406            "/tests/ref/vim_large_window_scroll/size.json"
2407        ));
2408
2409        let mut grid: Grid<Cell> = json::from_str(&serialized_grid).unwrap();
2410        let size: SizeInfo = json::from_str(&serialized_size).unwrap();
2411
2412        let config = MockConfig::default();
2413
2414        let mut terminal = Term::new(&config, &size, Clipboard::new_nop(), Mock);
2415        mem::swap(&mut terminal.grid, &mut grid);
2416
2417        b.iter(|| {
2418            let iter = terminal.renderable_cells(&config);
2419            for cell in iter {
2420                test::black_box(cell);
2421            }
2422        })
2423    }
2424}