diff options
author | rbong <rbong@users.noreply.github.com> | 2019-08-19 15:56:27 -0400 |
---|---|---|
committer | Christian Duerr <contact@christianduerr.com> | 2019-08-19 19:56:27 +0000 |
commit | 629ea247cdccc33c6df0037f357ba9be48c7178d (patch) | |
tree | f391752e1824971747468e334cf6a38945adde6f /alacritty_terminal/src/ansi.rs | |
parent | a8692983f522e9bd37911d14dc441391a7ee2f50 (diff) | |
download | alacritty-629ea247cdccc33c6df0037f357ba9be48c7178d.tar.gz alacritty-629ea247cdccc33c6df0037f357ba9be48c7178d.zip |
Follow xparsecolor spec in escape sequences
Escape sequences in xterm are parsed according to xparsecolor.
xparsecolor supports 1, 2, 3, and 4 digit hex colors.
Previously, only 2 digits were supported.
This also fixes a bug where "fX" was parsed as "0xf", where X is an invalid character.
The response to a request for fg/bg must be a valid escape sequence.
The current response uses 4-digit hex, which was previously invalid.
Diffstat (limited to 'alacritty_terminal/src/ansi.rs')
-rw-r--r-- | alacritty_terminal/src/ansi.rs | 129 |
1 files changed, 70 insertions, 59 deletions
diff --git a/alacritty_terminal/src/ansi.rs b/alacritty_terminal/src/ansi.rs index 57ad17fd..3189b300 100644 --- a/alacritty_terminal/src/ansi.rs +++ b/alacritty_terminal/src/ansi.rs @@ -24,63 +24,56 @@ use vte; use crate::term::color::Rgb; -// Parse color arguments -// -// Expect that color argument looks like "rgb:xx/xx/xx" or "#xxxxxx" +// Parse colors in XParseColor format +fn xparse_color(color: &[u8]) -> Option<Rgb> { + if !color.is_empty() && color[0] == b'#' { + let len = color.len().saturating_sub(1); + parse_legacy_color(&color[1..len]) + } else if color.len() >= 4 && &color[..4] == b"rgb:" { + let len = color.len().saturating_sub(1); + parse_rgb_color(&color[4..len]) + } else { + None + } +} + +// Parse colors in `rgb:r(rrr)/g(ggg)/b(bbb)` format fn parse_rgb_color(color: &[u8]) -> Option<Rgb> { - let mut iter = color.iter(); + let colors = str::from_utf8(color).ok()?.split('/').collect::<Vec<_>>(); - macro_rules! next { - () => { - iter.next().map(|v| *v as char) - }; + if colors.len() != 3 { + return None; } - macro_rules! parse_hex { - () => {{ - let mut digit: u8 = 0; - let next = next!().and_then(|v| v.to_digit(16)); - if let Some(value) = next { - digit = value as u8; - } + // Scale values instead of filling with `0`s + let scale = |input: &str| { + let max = u32::pow(16, input.len() as u32) - 1; + let value = u32::from_str_radix(input, 16).ok()?; + Some((255 * value / max) as u8) + }; - let next = next!().and_then(|v| v.to_digit(16)); - if let Some(value) = next { - digit <<= 4; - digit += value as u8; - } - digit - }}; - } + Some(Rgb { + r: scale(colors[0])?, + g: scale(colors[1])?, + b: scale(colors[2])?, + }) +} - match next!() { - Some('r') => { - if next!() != Some('g') { - return None; - } - if next!() != Some('b') { - return None; - } - if next!() != Some(':') { - return None; - } +// Parse colors in `#r(rrr)g(ggg)b(bbb)` format +fn parse_legacy_color(color: &[u8]) -> Option<Rgb> { + let item_len = color.len() / 3; - let r = parse_hex!(); - let val = next!(); - if val != Some('/') { - return None; - } - let g = parse_hex!(); - if next!() != Some('/') { - return None; - } - let b = parse_hex!(); + // Truncate/Fill to two byte precision + let color_from_slice = |slice: &[u8]| { + let col = usize::from_str_radix(str::from_utf8(slice).ok()?, 16).ok()? << 4; + Some((col >> (4 * slice.len().saturating_sub(1))) as u8) + }; - Some(Rgb { r, g, b }) - }, - Some('#') => Some(Rgb { r: parse_hex!(), g: parse_hex!(), b: parse_hex!() }), - _ => None, - } + Some(Rgb { + r: color_from_slice(&color[0..item_len])?, + g: color_from_slice(&color[item_len..item_len * 2])?, + b: color_from_slice(&color[item_len * 2..])?, + }) } fn parse_number(input: &[u8]) -> Option<u8> { @@ -781,7 +774,7 @@ where if params.len() > 1 && params.len() % 2 != 0 { for chunk in params[1..].chunks(2) { let index = parse_number(chunk[0]); - let color = parse_rgb_color(chunk[1]); + let color = xparse_color(chunk[1]); if let (Some(i), Some(c)) = (index, color) { self.handler.set_color(i as usize, c); return; @@ -806,7 +799,7 @@ where break; } - if let Some(color) = parse_rgb_color(param) { + if let Some(color) = xparse_color(param) { self.handler.set_color(index, color); } else if param == b"?" { self.handler.dynamic_color_sequence(writer, dynamic_code, index); @@ -1203,7 +1196,7 @@ fn attrs_from_sgr_parameters(parameters: &[i64]) -> Vec<Option<Attr>> { 37 => Some(Attr::Foreground(Color::Named(NamedColor::White))), 38 => { let mut start = 0; - if let Some(color) = parse_color(¶meters[i..], &mut start) { + if let Some(color) = parse_sgr_color(¶meters[i..], &mut start) { i += start; Some(Attr::Foreground(color)) } else { @@ -1221,7 +1214,7 @@ fn attrs_from_sgr_parameters(parameters: &[i64]) -> Vec<Option<Attr>> { 47 => Some(Attr::Background(Color::Named(NamedColor::White))), 48 => { let mut start = 0; - if let Some(color) = parse_color(¶meters[i..], &mut start) { + if let Some(color) = parse_sgr_color(¶meters[i..], &mut start) { i += start; Some(Attr::Background(color)) } else { @@ -1256,7 +1249,7 @@ fn attrs_from_sgr_parameters(parameters: &[i64]) -> Vec<Option<Attr>> { } /// Parse a color specifier from list of attributes -fn parse_color(attrs: &[i64], i: &mut usize) -> Option<Color> { +fn parse_sgr_color(attrs: &[i64], i: &mut usize) -> Option<Color> { if attrs.len() < 2 { return None; } @@ -1456,7 +1449,7 @@ pub mod C1 { #[cfg(test)] mod tests { use super::{ - parse_number, parse_rgb_color, Attr, CharsetIndex, Color, Handler, Processor, + parse_number, xparse_color, Attr, CharsetIndex, Color, Handler, Processor, StandardCharset, TermInfo, }; use crate::index::{Column, Line}; @@ -1623,13 +1616,31 @@ mod tests { } #[test] - fn parse_valid_rgb_color() { - assert_eq!(parse_rgb_color(b"rgb:11/aa/ff"), Some(Rgb { r: 0x11, g: 0xaa, b: 0xff })); + fn parse_valid_rgb_colors() { + assert_eq!(xparse_color(b"rgb:f/e/d\x07"), Some(Rgb { r: 0xff, g: 0xee, b: 0xdd })); + assert_eq!(xparse_color(b"rgb:11/aa/ff\x07"), Some(Rgb { r: 0x11, g: 0xaa, b: 0xff })); + assert_eq!(xparse_color(b"rgb:f/ed1/cb23\x07"), Some(Rgb { r: 0xff, g: 0xec, b: 0xca })); + assert_eq!(xparse_color(b"rgb:ffff/0/0\x07"), Some(Rgb { r: 0xff, g: 0x0, b: 0x0 })); + } + + #[test] + fn parse_valid_legacy_rgb_colors() { + assert_eq!(xparse_color(b"#1af\x07"), Some(Rgb { r: 0x10, g: 0xa0, b: 0xf0 })); + assert_eq!(xparse_color(b"#11aaff\x07"), Some(Rgb { r: 0x11, g: 0xaa, b: 0xff })); + assert_eq!(xparse_color(b"#110aa0ff0\x07"), Some(Rgb { r: 0x11, g: 0xaa, b: 0xff })); + assert_eq!(xparse_color(b"#1100aa00ff00\x07"), Some(Rgb { r: 0x11, g: 0xaa, b: 0xff })); + } + + #[test] + fn parse_invalid_rgb_colors() { + assert_eq!(xparse_color(b"rgb:0//\x07"), None); + assert_eq!(xparse_color(b"rgb://///\x07"), None); } #[test] - fn parse_valid_rgb_color2() { - assert_eq!(parse_rgb_color(b"#11aaff"), Some(Rgb { r: 0x11, g: 0xaa, b: 0xff })); + fn parse_invalid_legacy_rgb_colors() { + assert_eq!(xparse_color(b"#\x07"), None); + assert_eq!(xparse_color(b"#f\x07"), None); } #[test] |