summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rw-r--r--alacritty_terminal/src/ansi.rs246
-rw-r--r--alacritty_terminal/src/term/mod.rs1
3 files changed, 148 insertions, 101 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 68389f00..2bedd1f6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -82,6 +82,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Mouse protocols/encodings not being mutually exclusive within themselves
- Escape `CSI Ps M` deleting lines above cursor when at the bottom of the viewport
- Cell reset not clearing underline, strikeout and foreground color
+- Escape `CSI Ps c` honored with a wrong `Ps`
+- Ignore `ESC` escapes with invalid intermediates
### Removed
diff --git a/alacritty_terminal/src/ansi.rs b/alacritty_terminal/src/ansi.rs
index a889862f..c2388540 100644
--- a/alacritty_terminal/src/ansi.rs
+++ b/alacritty_terminal/src/ansi.rs
@@ -943,7 +943,9 @@ where
('B', None) | ('e', None) => {
handler.move_down(Line(arg_or_default!(idx: 0, default: 1) as usize))
},
- ('c', None) => handler.identify_terminal(writer),
+ ('c', None) if arg_or_default!(idx: 0, default: 0) == 0 => {
+ handler.identify_terminal(writer)
+ },
('C', None) | ('a', None) => {
handler.move_forward(Column(arg_or_default!(idx: 0, default: 1) as usize))
},
@@ -1104,8 +1106,8 @@ where
}
macro_rules! configure_charset {
- ($charset:path) => {{
- let index: CharsetIndex = match intermediates.first().cloned() {
+ ($charset:path, $intermediate:expr) => {{
+ let index: CharsetIndex = match $intermediate {
Some(b'(') => CharsetIndex::G0,
Some(b')') => CharsetIndex::G1,
Some(b'*') => CharsetIndex::G2,
@@ -1119,29 +1121,27 @@ where
}};
}
- match byte {
- b'B' => configure_charset!(StandardCharset::Ascii),
- b'D' => self.handler.linefeed(),
- b'E' => {
+ match (byte, intermediates.get(0)) {
+ (b'B', intermediate) => configure_charset!(StandardCharset::Ascii, intermediate),
+ (b'D', None) => self.handler.linefeed(),
+ (b'E', None) => {
self.handler.linefeed();
self.handler.carriage_return();
},
- b'H' => self.handler.set_horizontal_tabstop(),
- b'M' => self.handler.reverse_index(),
- b'Z' => self.handler.identify_terminal(self.writer),
- b'c' => self.handler.reset_state(),
- b'0' => configure_charset!(StandardCharset::SpecialCharacterAndLineDrawing),
- b'7' => self.handler.save_cursor_position(),
- b'8' => {
- if !intermediates.is_empty() && intermediates[0] == b'#' {
- self.handler.decaln();
- } else {
- self.handler.restore_cursor_position();
- }
+ (b'H', None) => self.handler.set_horizontal_tabstop(),
+ (b'M', None) => self.handler.reverse_index(),
+ (b'Z', None) => self.handler.identify_terminal(self.writer),
+ (b'c', None) => self.handler.reset_state(),
+ (b'0', intermediate) => {
+ configure_charset!(StandardCharset::SpecialCharacterAndLineDrawing, intermediate)
},
- b'=' => self.handler.set_keypad_application_mode(),
- b'>' => self.handler.unset_keypad_application_mode(),
- b'\\' => (), // String terminator, do nothing (parser handles as string terminator)
+ (b'7', None) => self.handler.save_cursor_position(),
+ (b'8', Some(b'#')) => self.handler.decaln(),
+ (b'8', None) => self.handler.restore_cursor_position(),
+ (b'=', None) => self.handler.set_keypad_application_mode(),
+ (b'>', None) => self.handler.unset_keypad_application_mode(),
+ // String terminator, do nothing (parser handles as string terminator)
+ (b'\\', None) => (),
_ => unhandled!(),
}
}
@@ -1446,129 +1446,173 @@ mod tests {
use crate::term::color::Rgb;
use std::io;
- /// The /dev/null of `io::Write`
- struct Void;
+ struct MockHandler {
+ index: CharsetIndex,
+ charset: StandardCharset,
+ attr: Option<Attr>,
+ identity_reported: bool,
+ }
- impl io::Write for Void {
- fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
- Ok(bytes.len())
+ impl Handler for MockHandler {
+ fn terminal_attribute(&mut self, attr: Attr) {
+ self.attr = Some(attr);
}
- fn flush(&mut self) -> io::Result<()> {
- Ok(())
+ fn configure_charset(&mut self, index: CharsetIndex, charset: StandardCharset) {
+ self.index = index;
+ self.charset = charset;
}
- }
- #[derive(Default)]
- struct AttrHandler {
- attr: Option<Attr>,
- }
+ fn set_active_charset(&mut self, index: CharsetIndex) {
+ self.index = index;
+ }
- impl Handler for AttrHandler {
- fn terminal_attribute(&mut self, attr: Attr) {
- self.attr = Some(attr);
+ fn identify_terminal<W: io::Write>(&mut self, _: &mut W) {
+ self.identity_reported = true;
+ }
+
+ fn reset_state(&mut self) {
+ *self = Self::default();
}
}
- impl TermInfo for AttrHandler {
+ impl TermInfo for MockHandler {
fn lines(&self) -> Line {
- Line(24)
+ Line(200)
}
fn cols(&self) -> Column {
- Column(80)
+ Column(90)
+ }
+ }
+
+ impl Default for MockHandler {
+ fn default() -> MockHandler {
+ MockHandler {
+ index: CharsetIndex::G0,
+ charset: StandardCharset::Ascii,
+ attr: None,
+ identity_reported: false,
+ }
}
}
#[test]
fn parse_control_attribute() {
- static BYTES: &[u8] = &[0x1b, 0x5b, 0x31, 0x6d];
+ static BYTES: &[u8] = &[0x1b, b'[', b'1', b'm'];
let mut parser = Processor::new();
- let mut handler = AttrHandler::default();
+ let mut handler = MockHandler::default();
for byte in &BYTES[..] {
- parser.advance(&mut handler, *byte, &mut Void);
+ parser.advance(&mut handler, *byte, &mut io::sink());
}
assert_eq!(handler.attr, Some(Attr::Bold));
}
#[test]
- fn parse_truecolor_attr() {
- static BYTES: &[u8] = &[
- 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x32, 0x3b, 0x31, 0x32, 0x38, 0x3b, 0x36, 0x36, 0x3b,
- 0x32, 0x35, 0x35, 0x6d,
- ];
+ fn parse_terminal_identity_csi() {
+ let bytes: &[u8] = &[0x1b, b'[', b'1', b'c'];
let mut parser = Processor::new();
- let mut handler = AttrHandler::default();
+ let mut handler = MockHandler::default();
- for byte in &BYTES[..] {
- parser.advance(&mut handler, *byte, &mut Void);
+ for byte in &bytes[..] {
+ parser.advance(&mut handler, *byte, &mut io::sink());
}
- let spec = Rgb { r: 128, g: 66, b: 255 };
+ assert!(!handler.identity_reported);
+ handler.reset_state();
- assert_eq!(handler.attr, Some(Attr::Foreground(Color::Spec(spec))));
+ let bytes: &[u8] = &[0x1b, b'[', b'c'];
+
+ for byte in &bytes[..] {
+ parser.advance(&mut handler, *byte, &mut io::sink());
+ }
+
+ assert!(handler.identity_reported);
+ handler.reset_state();
+
+ let bytes: &[u8] = &[0x1b, b'[', b'0', b'c'];
+
+ for byte in &bytes[..] {
+ parser.advance(&mut handler, *byte, &mut io::sink());
+ }
+
+ assert!(handler.identity_reported);
}
- /// No exactly a test; useful for debugging
#[test]
- fn parse_zsh_startup() {
- static BYTES: &[u8] = &[
- 0x1b, 0x5b, 0x31, 0x6d, 0x1b, 0x5b, 0x37, 0x6d, 0x25, 0x1b, 0x5b, 0x32, 0x37, 0x6d,
- 0x1b, 0x5b, 0x31, 0x6d, 0x1b, 0x5b, 0x30, 0x6d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x0d, 0x20, 0x0d, 0x0d, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x32,
- 0x37, 0x6d, 0x1b, 0x5b, 0x32, 0x34, 0x6d, 0x1b, 0x5b, 0x4a, 0x6a, 0x77, 0x69, 0x6c,
- 0x6d, 0x40, 0x6a, 0x77, 0x69, 0x6c, 0x6d, 0x2d, 0x64, 0x65, 0x73, 0x6b, 0x20, 0x1b,
- 0x5b, 0x30, 0x31, 0x3b, 0x33, 0x32, 0x6d, 0xe2, 0x9e, 0x9c, 0x20, 0x1b, 0x5b, 0x30,
- 0x31, 0x3b, 0x33, 0x32, 0x6d, 0x20, 0x1b, 0x5b, 0x33, 0x36, 0x6d, 0x7e, 0x2f, 0x63,
- 0x6f, 0x64, 0x65,
- ];
+ fn parse_terminal_identity_esc() {
+ let bytes: &[u8] = &[0x1b, b'Z'];
- let mut handler = AttrHandler::default();
let mut parser = Processor::new();
+ let mut handler = MockHandler::default();
- for byte in &BYTES[..] {
- parser.advance(&mut handler, *byte, &mut Void);
+ for byte in &bytes[..] {
+ parser.advance(&mut handler, *byte, &mut io::sink());
}
- }
- struct CharsetHandler {
- index: CharsetIndex,
- charset: StandardCharset,
- }
+ assert!(handler.identity_reported);
+ handler.reset_state();
+
+ let bytes: &[u8] = &[0x1b, b'#', b'Z'];
+
+ let mut parser = Processor::new();
+ let mut handler = MockHandler::default();
- impl Default for CharsetHandler {
- fn default() -> CharsetHandler {
- CharsetHandler { index: CharsetIndex::G0, charset: StandardCharset::Ascii }
+ for byte in &bytes[..] {
+ parser.advance(&mut handler, *byte, &mut io::sink());
}
+
+ assert!(!handler.identity_reported);
+ handler.reset_state();
}
- impl Handler for CharsetHandler {
- fn configure_charset(&mut self, index: CharsetIndex, charset: StandardCharset) {
- self.index = index;
- self.charset = charset;
- }
+ #[test]
+ fn parse_truecolor_attr() {
+ static BYTES: &[u8] = &[
+ 0x1b, b'[', b'3', b'8', b';', b'2', b';', b'1', b'2', b'8', b';', b'6', b'6', b';',
+ b'2', b'5', b'5', b'm',
+ ];
- fn set_active_charset(&mut self, index: CharsetIndex) {
- self.index = index;
+ let mut parser = Processor::new();
+ let mut handler = MockHandler::default();
+
+ for byte in &BYTES[..] {
+ parser.advance(&mut handler, *byte, &mut io::sink());
}
+
+ let spec = Rgb { r: 128, g: 66, b: 255 };
+
+ assert_eq!(handler.attr, Some(Attr::Foreground(Color::Spec(spec))));
}
- impl TermInfo for CharsetHandler {
- fn lines(&self) -> Line {
- Line(200)
- }
+ /// No exactly a test; useful for debugging
+ #[test]
+ fn parse_zsh_startup() {
+ static BYTES: &[u8] = &[
+ 0x1b, b'[', b'1', b'm', 0x1b, b'[', b'7', b'm', b'%', 0x1b, b'[', b'2', b'7', b'm',
+ 0x1b, b'[', b'1', b'm', 0x1b, b'[', b'0', b'm', b' ', b' ', b' ', b' ', b' ', b' ',
+ b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ',
+ b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ',
+ b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ',
+ b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ',
+ b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ', b' ',
+ b' ', b' ', b' ', b'\r', b' ', b'\r', b'\r', 0x1b, b'[', b'0', b'm', 0x1b, b'[', b'2',
+ b'7', b'm', 0x1b, b'[', b'2', b'4', b'm', 0x1b, b'[', b'J', b'j', b'w', b'i', b'l',
+ b'm', b'@', b'j', b'w', b'i', b'l', b'm', b'-', b'd', b'e', b's', b'k', b' ', 0x1b,
+ b'[', b'0', b'1', b';', b'3', b'2', b'm', 0xe2, 0x9e, 0x9c, b' ', 0x1b, b'[', b'0',
+ b'1', b';', b'3', b'2', b'm', b' ', 0x1b, b'[', b'3', b'6', b'm', b'~', b'/', b'c',
+ b'o', b'd', b'e',
+ ];
- fn cols(&self) -> Column {
- Column(90)
+ let mut handler = MockHandler::default();
+ let mut parser = Processor::new();
+
+ for byte in &BYTES[..] {
+ parser.advance(&mut handler, *byte, &mut io::sink());
}
}
@@ -1576,10 +1620,10 @@ mod tests {
fn parse_designate_g0_as_line_drawing() {
static BYTES: &[u8] = &[0x1b, b'(', b'0'];
let mut parser = Processor::new();
- let mut handler = CharsetHandler::default();
+ let mut handler = MockHandler::default();
for byte in &BYTES[..] {
- parser.advance(&mut handler, *byte, &mut Void);
+ parser.advance(&mut handler, *byte, &mut io::sink());
}
assert_eq!(handler.index, CharsetIndex::G0);
@@ -1588,19 +1632,19 @@ mod tests {
#[test]
fn parse_designate_g1_as_line_drawing_and_invoke() {
- static BYTES: &[u8] = &[0x1b, 0x29, 0x30, 0x0e];
+ static BYTES: &[u8] = &[0x1b, b')', b'0', 0x0e];
let mut parser = Processor::new();
- let mut handler = CharsetHandler::default();
+ let mut handler = MockHandler::default();
for byte in &BYTES[..3] {
- parser.advance(&mut handler, *byte, &mut Void);
+ parser.advance(&mut handler, *byte, &mut io::sink());
}
assert_eq!(handler.index, CharsetIndex::G1);
assert_eq!(handler.charset, StandardCharset::SpecialCharacterAndLineDrawing);
- let mut handler = CharsetHandler::default();
- parser.advance(&mut handler, BYTES[3], &mut Void);
+ let mut handler = MockHandler::default();
+ parser.advance(&mut handler, BYTES[3], &mut io::sink());
assert_eq!(handler.index, CharsetIndex::G1);
}
diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs
index ce63c407..8d69fb3b 100644
--- a/alacritty_terminal/src/term/mod.rs
+++ b/alacritty_terminal/src/term/mod.rs
@@ -1397,6 +1397,7 @@ impl<T: EventListener> ansi::Handler for Term<T> {
#[inline]
fn identify_terminal<W: io::Write>(&mut self, writer: &mut W) {
+ trace!("Reporting terminal identity");
let _ = writer.write_all(b"\x1b[?6c");
}