Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [Unreleased]

### Added
- SGR double underline (attribute 21), cleared by SGR 24 along with single underline
- add `--maximized` and `--fullscreen` flags to start the window in that mode

### Changed
Expand Down
1 change: 1 addition & 0 deletions src/renderer/draw_fns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub(super) static BLANK_CELL: Cell = Cell {
overline: false,
reverse: false,
blink: false,
double_underline: false,
wide: false,
wide_cont: false,
url: None,
Expand Down
27 changes: 9 additions & 18 deletions src/renderer/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1018,27 +1018,18 @@ fn draw_cell_decorations(
dim_color(color_u32(fg), dim_factor)
};

let hline =
|buf: &mut [u32], y: u32| draw_clipped_hline(buf, buf_width, y, cell_x, draw_w, clip, fg32);
if cell.underline {
draw_clipped_hline(
buf,
buf_width,
cell_y + m.cell_height.saturating_sub(2),
cell_x,
draw_w,
clip,
fg32,
);
hline(buf, cell_y + m.cell_height.saturating_sub(2));
}
if cell.double_underline {
let base = cell_y + m.cell_height.saturating_sub(1);
hline(buf, base);
hline(buf, base.saturating_sub(2));
}
if cell.strikethrough {
draw_clipped_hline(
buf,
buf_width,
cell_y + m.cell_height / 2,
cell_x,
draw_w,
clip,
fg32,
);
hline(buf, cell_y + m.cell_height / 2);
}
if cell.overline {
draw_clipped_hline(buf, buf_width, cell_y, cell_x, draw_w, clip, fg32);
Expand Down
12 changes: 12 additions & 0 deletions src/renderer/text_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,18 @@ fn draw_pane_with_underline_cell_does_not_panic() {
do_draw(&mut r, &[pane], &InputMode::Insert);
}

#[test]
fn draw_pane_with_double_underline_cell_does_not_panic() {
let mut r = make_renderer();
let m = r.make_metrics(Physical(16.0));
let (cols, rows) = m.grid_size_for(800, 600u32.saturating_sub(44));
let mut grid = make_grid(cols, rows);
grid.write_char('D');
grid.cell_mut(0, 0).double_underline = true;
let pane = make_pane(&grid, &m);
do_draw(&mut r, &[pane], &InputMode::Insert);
}

#[test]
fn draw_pane_osc8_link_without_hover_does_not_panic() {
let mut r = make_renderer();
Expand Down
13 changes: 13 additions & 0 deletions src/terminal/grid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ pub struct Cell {
pub overline: bool,
pub reverse: bool,
pub blink: bool,
pub double_underline: bool,
/// True when this is the left half of a 2-column wide character.
pub wide: bool,
/// True when this is the right (placeholder) half of a wide character.
Expand All @@ -88,6 +89,7 @@ impl Default for Cell {
overline: false,
reverse: false,
blink: false,
double_underline: false,
wide: false,
wide_cont: false,
url: None,
Expand All @@ -108,6 +110,7 @@ fn cell_with_colors(c: char, fg: Color, bg: Color) -> Cell {
overline: false,
reverse: false,
blink: false,
double_underline: false,
wide: false,
wide_cont: false,
url: None,
Expand All @@ -127,6 +130,7 @@ struct SavedCursor {
overline: bool,
reverse: bool,
blink: bool,
double_underline: bool,
}

struct SavedScreen {
Expand All @@ -148,6 +152,7 @@ struct SavedScreen {
overline: bool,
reverse: bool,
blink: bool,
double_underline: bool,
scrollback: VecDeque<Vec<Cell>>,
scrollback_wrapped: VecDeque<bool>,
row_wrapped: Vec<bool>,
Expand All @@ -173,6 +178,7 @@ pub struct Grid {
pub overline: bool,
pub reverse: bool,
pub blink: bool,
pub double_underline: bool,
// DECSC/DECRC saved cursor (ESC 7 / ESC 8)
decsc: Option<SavedCursor>,
// Scrollback: lines that have scrolled off the top (oldest first)
Expand Down Expand Up @@ -268,6 +274,7 @@ impl Grid {
overline: false,
reverse: false,
blink: false,
double_underline: false,
decsc: None,
scrollback: VecDeque::new(),
scrollback_wrapped: VecDeque::new(),
Expand Down Expand Up @@ -326,6 +333,7 @@ impl Grid {
overline: self.overline,
reverse: self.reverse,
blink: self.blink,
double_underline: self.double_underline,
scrollback: std::mem::take(&mut self.scrollback),
scrollback_wrapped: std::mem::take(&mut self.scrollback_wrapped),
row_wrapped: std::mem::replace(&mut self.row_wrapped, vec![false; self.rows]),
Expand Down Expand Up @@ -375,6 +383,7 @@ impl Grid {
self.overline = saved.overline;
self.reverse = saved.reverse;
self.blink = saved.blink;
self.double_underline = saved.double_underline;
self.scrollback = saved.scrollback;
self.scrollback_wrapped = saved.scrollback_wrapped;
self.row_wrapped = saved.row_wrapped;
Expand Down Expand Up @@ -638,6 +647,7 @@ impl Grid {
overline: self.overline,
reverse: self.reverse,
blink: self.blink,
double_underline: self.double_underline,
wide,
url: self.current_url.clone(),
..cell_with_colors(c, self.fg, self.bg)
Expand Down Expand Up @@ -974,6 +984,7 @@ impl Grid {
self.overline = false;
self.reverse = false;
self.blink = false;
self.double_underline = false;
}

pub fn save_cursor(&mut self) {
Expand All @@ -990,6 +1001,7 @@ impl Grid {
overline: self.overline,
reverse: self.reverse,
blink: self.blink,
double_underline: self.double_underline,
});
}

Expand All @@ -1007,6 +1019,7 @@ impl Grid {
self.overline = s.overline;
self.reverse = s.reverse;
self.blink = s.blink;
self.double_underline = s.double_underline;
}
}

Expand Down
34 changes: 15 additions & 19 deletions src/terminal/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,27 +66,19 @@ struct Performer<'a> {

impl Performer<'_> {
fn handle_dec_private_modes(&mut self, action: char, p0: u16) {
// Only DECSET (h) / DECRST (l) toggle modes; the `('h' | 'l', _)` patterns
// keep other actions out of these arms, so `on` is only used when relevant.
let on = action == 'h';
match (action, p0) {
('h', 1) => self.grid.application_cursor_keys = true,
('l', 1) => self.grid.application_cursor_keys = false,
('h', 7) => self.grid.autowrap = true,
('l', 7) => self.grid.autowrap = false,
('h', 25) => self.grid.cursor_visible = true,
('l', 25) => self.grid.cursor_visible = false,
('h', 1000) => self.grid.mouse_mode = 1000,
('l', 1000) => self.grid.mouse_mode = 0,
('h', 1002) => self.grid.mouse_mode = 1002,
('l', 1002) => self.grid.mouse_mode = 0,
('h', 1003) => self.grid.mouse_mode = 1003,
('l', 1003) => self.grid.mouse_mode = 0,
('h', 1004) => self.grid.focus_report = true,
('l', 1004) => self.grid.focus_report = false,
('h', 1006) => self.grid.mouse_sgr = true,
('l', 1006) => self.grid.mouse_sgr = false,
('h' | 'l', 1) => self.grid.application_cursor_keys = on,
('h' | 'l', 7) => self.grid.autowrap = on,
('h' | 'l', 25) => self.grid.cursor_visible = on,
('h' | 'l', 1000 | 1002 | 1003) => self.grid.mouse_mode = if on { p0 } else { 0 },
('h' | 'l', 1004) => self.grid.focus_report = on,
('h' | 'l', 1006) => self.grid.mouse_sgr = on,
('h' | 'l', 2004) => self.grid.bracketed_paste = on,
('h', 1049) => self.grid.enter_alternate_screen(),
('l', 1049) => self.grid.exit_alternate_screen(),
('h', 2004) => self.grid.bracketed_paste = true,
('l', 2004) => self.grid.bracketed_paste = false,
_ => {}
}
}
Expand Down Expand Up @@ -178,8 +170,12 @@ impl Performer<'_> {
self.grid.bold = false;
self.grid.dim = false;
}
21 => self.grid.double_underline = true,
23 => self.grid.italic = false,
24 => self.grid.underline = false,
24 => {
self.grid.underline = false;
self.grid.double_underline = false;
}
25 => self.grid.blink = false,
27 => self.grid.reverse = false,
29 => self.grid.strikethrough = false,
Expand Down
27 changes: 27 additions & 0 deletions src/terminal/parser_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,33 @@ fn sgr_reset_clears_attributes() {
assert!(!p.grid.blink);
}

#[test]
fn sgr_double_underline_stamped_on_cell() {
let mut p = make_parser(10, 5);
p.process(b"\x1b[21mA");
assert!(p.grid.cell(0, 0).double_underline);
assert!(p.grid.double_underline);
}

#[test]
fn sgr_24_clears_single_and_double_underline() {
let mut p = make_parser(10, 5);
p.process(b"\x1b[4;21m"); // both underline and double underline on
assert!(p.grid.underline);
assert!(p.grid.double_underline);
p.process(b"\x1b[24m");
assert!(!p.grid.underline);
assert!(!p.grid.double_underline);
}

#[test]
fn sgr_reset_clears_double_underline() {
let mut p = make_parser(10, 5);
p.process(b"\x1b[21m");
p.process(b"\x1b[0m");
assert!(!p.grid.double_underline);
}

#[test]
fn erase_in_line_to_end() {
let mut p = make_parser(10, 5);
Expand Down