From 32ff8ba96e3948852c599966f5b04fbe10b0a469 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Mon, 19 Dec 2016 16:40:44 +0100 Subject: [PATCH] Replace UnknownCsi with generalized Event::Unsupported (#80) --- src/event.rs | 367 ++++++++++++++++++++++++++------------------------- src/input.rs | 83 ++++++++---- 2 files changed, 243 insertions(+), 207 deletions(-) diff --git a/src/event.rs b/src/event.rs index 44a139e..c65e827 100644 --- a/src/event.rs +++ b/src/event.rs @@ -12,10 +12,7 @@ pub enum Event { /// A mouse button press, release or wheel use at specific coordinates. Mouse(MouseEvent), /// An event that cannot currently be evaluated. - Unsupported, - /// A CSI sequence unrecognized by termion. Does not inlude the leading `^[`. - UnknownCsi(Vec), - + Unsupported(Vec), } /// A mouse related event. @@ -97,210 +94,218 @@ pub enum Key { Esc, #[doc(hidden)] - __IsNotComplete + __IsNotComplete, } /// Parse an Event from `item` and possibly subsequent bytes through `iter`. -pub fn parse_event(item: Result, iter: &mut I) -> Result -where I: Iterator> +pub fn parse_event(item: u8, iter: &mut I) -> Result + where I: Iterator> { - let error = Err(Error::new(ErrorKind::Other, "Could not parse an event")); + let error = Error::new(ErrorKind::Other, "Could not parse an event"); match item { - Ok(b'\x1B') => { + b'\x1B' => { + // This is an escape character, leading a control sequence. Ok(match iter.next() { Some(Ok(b'O')) => { match iter.next() { // F1-F4 - Some(Ok(val @ b'P' ... b'S')) => Event::Key(Key::F(1 + val - b'P')), - _ => return error, + Some(Ok(val @ b'P'...b'S')) => Event::Key(Key::F(1 + val - b'P')), + _ => return Err(error), } } Some(Ok(b'[')) => { // This is a CSI sequence. - match iter.next() { - Some(Ok(b'D')) => Event::Key(Key::Left), - Some(Ok(b'C')) => Event::Key(Key::Right), - Some(Ok(b'A')) => Event::Key(Key::Up), - Some(Ok(b'B')) => Event::Key(Key::Down), - Some(Ok(b'H')) => Event::Key(Key::Home), - Some(Ok(b'F')) => Event::Key(Key::End), - Some(Ok(b'M')) => { - // X10 emulation mouse encoding: ESC [ CB Cx Cy (6 characters only). - let cb = iter.next().unwrap().unwrap() as i8 - 32; - // (1, 1) are the coords for upper left. - let cx = (iter.next().unwrap().unwrap() as u8).saturating_sub(32) as u16; - let cy = (iter.next().unwrap().unwrap() as u8).saturating_sub(32) as u16; - Event::Mouse(match cb & 0b11 { - 0 => { - if cb & 0x40 != 0 { - MouseEvent::Press(MouseButton::WheelUp, cx, cy) - } else { - MouseEvent::Press(MouseButton::Left, cx, cy) - } - } - 1 => { - if cb & 0x40 != 0 { - MouseEvent::Press(MouseButton::WheelDown, cx, cy) - } else { - MouseEvent::Press(MouseButton::Middle, cx, cy) - } - } - 2 => MouseEvent::Press(MouseButton::Right, cx, cy), - 3 => MouseEvent::Release(cx, cy), - _ => return error, - }) - }, - Some(Ok(b'<')) => { - // xterm mouse encoding: - // ESC [ < Cb ; Cx ; Cy ; (M or m) - let mut buf = Vec::new(); - let mut c = iter.next().unwrap().unwrap(); - while match c { - b'm' | b'M' => false, - _ => true, - } { - buf.push(c); - c = iter.next().unwrap().unwrap(); - } - let str_buf = String::from_utf8(buf).unwrap(); - let nums = &mut str_buf.split(';'); - - let cb = nums.next().unwrap().parse::().unwrap(); - let cx = nums.next().unwrap().parse::().unwrap(); - let cy = nums.next().unwrap().parse::().unwrap(); - - let event = match cb { - 0...2 | 64...65 => { - let button = match cb { - 0 => MouseButton::Left, - 1 => MouseButton::Middle, - 2 => MouseButton::Right, - 64 => MouseButton::WheelUp, - 65 => MouseButton::WheelDown, - _ => return error, - }; - match c { - b'M' => MouseEvent::Press(button, cx, cy), - b'm' => MouseEvent::Release(cx, cy), - _ => return error, - } - } - 32 => MouseEvent::Hold(cx, cy), - _ => return error, - }; - - Event::Mouse(event) - }, - Some(Ok(c @ b'0'...b'9')) => { - // Numbered escape code. - let mut buf = Vec::new(); - buf.push(c); - let mut c = iter.next().unwrap().unwrap(); - // The final byte of a CSI sequence can be in the range 64-126, so - // let's keep reading anything else. - while c < 64 || c > 126 { - buf.push(c); - c = iter.next().unwrap().unwrap(); - } - // Include the terminal char in the buffer. - // We'll want it if we return an unknown sequence. - buf.push(c); - - match c { - // rxvt mouse encoding: - // ESC [ Cb ; Cx ; Cy ; M - b'M' => { - let str_buf = String::from_utf8(buf).unwrap(); - - // - let nums: Vec = str_buf[..str_buf.len()-1].split(';') - .map(|n| n.parse().unwrap()) - .collect(); - - let cb = nums[0]; - let cx = nums[1]; - let cy = nums[2]; - - let event = match cb { - 32 => MouseEvent::Press(MouseButton::Left, cx, cy), - 33 => MouseEvent::Press(MouseButton::Middle, cx, cy), - 34 => MouseEvent::Press(MouseButton::Right, cx, cy), - 35 => MouseEvent::Release(cx, cy), - 64 => MouseEvent::Hold(cx, cy), - 96 | - 97 => MouseEvent::Press(MouseButton::WheelUp, cx, cy), - _ => return Ok(Event::UnknownCsi(str_buf.into_bytes())), - }; - - Event::Mouse(event) - }, - // Special key code. - b'~' => { - let str_buf = String::from_utf8(buf).unwrap(); - - // This CSI sequence can be a list of semicolon-separated - // numbers. - let nums: Vec = str_buf[..str_buf.len()-1].split(';') - .map(|n| n.parse().unwrap()) - .collect(); - - if nums.is_empty() { - return Ok(Event::UnknownCsi(str_buf.into_bytes())); - } - - // TODO: handle multiple values for key modififiers (ex: values - // [3, 2] means Shift+Delete) - if nums.len() > 1 { - return Ok(Event::UnknownCsi(str_buf.into_bytes())); - } - - match nums[0] { - 1 | 7 => Event::Key(Key::Home), - 2 => Event::Key(Key::Insert), - 3 => Event::Key(Key::Delete), - 4 | 8 => Event::Key(Key::End), - 5 => Event::Key(Key::PageUp), - 6 => Event::Key(Key::PageDown), - v @ 11...15 => Event::Key(Key::F(v - 10)), - v @ 17...21 => Event::Key(Key::F(v - 11)), - v @ 23...24 => Event::Key(Key::F(v - 12)), - _ => return Ok(Event::UnknownCsi(str_buf.into_bytes())), - } - }, - _ => return Ok(Event::UnknownCsi(buf)), - } - }, - _ => return error, - } + parse_csi(iter).ok_or(error)? } Some(Ok(c)) => { let ch = parse_utf8_char(c, iter); Event::Key(Key::Alt(try!(ch))) } - Some(Err(_)) | None => return error, + Some(Err(_)) | None => return Err(error), }) } - Ok(b'\n') | Ok(b'\r') => Ok(Event::Key(Key::Char('\n'))), - Ok(b'\t') => Ok(Event::Key(Key::Char('\t'))), - Ok(b'\x7F') => Ok(Event::Key(Key::Backspace)), - Ok(c @ b'\x01'...b'\x1A') => Ok(Event::Key(Key::Ctrl((c as u8 - 0x1 + b'a') as char))), - Ok(c @ b'\x1C'...b'\x1F') => { - Ok(Event::Key(Key::Ctrl((c as u8 - 0x1C + b'4') as char))) - } - Ok(b'\0') => Ok(Event::Key(Key::Null)), - Ok(c) => { + b'\n' | b'\r' => Ok(Event::Key(Key::Char('\n'))), + b'\t' => Ok(Event::Key(Key::Char('\t'))), + b'\x7F' => Ok(Event::Key(Key::Backspace)), + c @ b'\x01'...b'\x1A' => Ok(Event::Key(Key::Ctrl((c as u8 - 0x1 + b'a') as char))), + c @ b'\x1C'...b'\x1F' => Ok(Event::Key(Key::Ctrl((c as u8 - 0x1C + b'4') as char))), + b'\0' => Ok(Event::Key(Key::Null)), + c => { Ok({ let ch = parse_utf8_char(c, iter); Event::Key(Key::Char(try!(ch))) }) } - Err(e) => Err(e), } } +/// Parses a CSI sequence, just after reading ^[ +/// +/// Returns None if an unrecognized sequence is found. +fn parse_csi(iter: &mut I) -> Option + where I: Iterator> +{ + Some(match iter.next() { + Some(Ok(b'D')) => Event::Key(Key::Left), + Some(Ok(b'C')) => Event::Key(Key::Right), + Some(Ok(b'A')) => Event::Key(Key::Up), + Some(Ok(b'B')) => Event::Key(Key::Down), + Some(Ok(b'H')) => Event::Key(Key::Home), + Some(Ok(b'F')) => Event::Key(Key::End), + Some(Ok(b'M')) => { + // X10 emulation mouse encoding: ESC [ CB Cx Cy (6 characters only). + let mut next = || iter.next().unwrap().unwrap(); + + let cb = next() as i8 - 32; + // (1, 1) are the coords for upper left. + let cx = next().saturating_sub(32) as u16; + let cy = next().saturating_sub(32) as u16; + Event::Mouse(match cb & 0b11 { + 0 => { + if cb & 0x40 != 0 { + MouseEvent::Press(MouseButton::WheelUp, cx, cy) + } else { + MouseEvent::Press(MouseButton::Left, cx, cy) + } + } + 1 => { + if cb & 0x40 != 0 { + MouseEvent::Press(MouseButton::WheelDown, cx, cy) + } else { + MouseEvent::Press(MouseButton::Middle, cx, cy) + } + } + 2 => MouseEvent::Press(MouseButton::Right, cx, cy), + 3 => MouseEvent::Release(cx, cy), + _ => return None, + }) + } + Some(Ok(b'<')) => { + // xterm mouse encoding: + // ESC [ < Cb ; Cx ; Cy ; (M or m) + let mut buf = Vec::new(); + let mut c = iter.next().unwrap().unwrap(); + while match c { + b'm' | b'M' => false, + _ => true, + } { + buf.push(c); + c = iter.next().unwrap().unwrap(); + } + let str_buf = String::from_utf8(buf).unwrap(); + let nums = &mut str_buf.split(';'); + + let cb = nums.next().unwrap().parse::().unwrap(); + let cx = nums.next().unwrap().parse::().unwrap(); + let cy = nums.next().unwrap().parse::().unwrap(); + + let event = match cb { + 0...2 | 64...65 => { + let button = match cb { + 0 => MouseButton::Left, + 1 => MouseButton::Middle, + 2 => MouseButton::Right, + 64 => MouseButton::WheelUp, + 65 => MouseButton::WheelDown, + _ => unreachable!(), + }; + match c { + b'M' => MouseEvent::Press(button, cx, cy), + b'm' => MouseEvent::Release(cx, cy), + _ => return None, + } + } + 32 => MouseEvent::Hold(cx, cy), + _ => return None, + }; + + Event::Mouse(event) + } + Some(Ok(c @ b'0'...b'9')) => { + // Numbered escape code. + let mut buf = Vec::new(); + buf.push(c); + let mut c = iter.next().unwrap().unwrap(); + // The final byte of a CSI sequence can be in the range 64-126, so + // let's keep reading anything else. + while c < 64 || c > 126 { + buf.push(c); + c = iter.next().unwrap().unwrap(); + } + + match c { + // rxvt mouse encoding: + // ESC [ Cb ; Cx ; Cy ; M + b'M' => { + let str_buf = String::from_utf8(buf).unwrap(); + + let nums: Vec = str_buf + .split(';') + .map(|n| n.parse().unwrap()) + .collect(); + + let cb = nums[0]; + let cx = nums[1]; + let cy = nums[2]; + + let event = match cb { + 32 => MouseEvent::Press(MouseButton::Left, cx, cy), + 33 => MouseEvent::Press(MouseButton::Middle, cx, cy), + 34 => MouseEvent::Press(MouseButton::Right, cx, cy), + 35 => MouseEvent::Release(cx, cy), + 64 => MouseEvent::Hold(cx, cy), + 96 | 97 => MouseEvent::Press(MouseButton::WheelUp, cx, cy), + _ => return None, + }; + + Event::Mouse(event) + } + // Special key code. + b'~' => { + let str_buf = String::from_utf8(buf).unwrap(); + + // This CSI sequence can be a list of semicolon-separated + // numbers. + let nums: Vec = str_buf + .split(';') + .map(|n| n.parse().unwrap()) + .collect(); + + if nums.is_empty() { + return None; + } + + // TODO: handle multiple values for key modififiers (ex: values + // [3, 2] means Shift+Delete) + if nums.len() > 1 { + return None; + } + + match nums[0] { + 1 | 7 => Event::Key(Key::Home), + 2 => Event::Key(Key::Insert), + 3 => Event::Key(Key::Delete), + 4 | 8 => Event::Key(Key::End), + 5 => Event::Key(Key::PageUp), + 6 => Event::Key(Key::PageDown), + v @ 11...15 => Event::Key(Key::F(v - 10)), + v @ 17...21 => Event::Key(Key::F(v - 11)), + v @ 23...24 => Event::Key(Key::F(v - 12)), + _ => return None, + } + } + _ => return None, + } + } + _ => return None, + }) + +} + /// Parse `c` as either a single byte ASCII char or a variable size UTF-8 char. fn parse_utf8_char(c: u8, iter: &mut I) -> Result - where I: Iterator> { + where I: Iterator> +{ let error = Err(Error::new(ErrorKind::Other, "Input character is not valid UTF-8")); if c.is_ascii() { Ok(c as char) @@ -311,9 +316,11 @@ fn parse_utf8_char(c: u8, iter: &mut I) -> Result loop { bytes.push(iter.next().unwrap().unwrap()); if let Ok(st) = str::from_utf8(bytes) { - return Ok(st.chars().next().unwrap()) + return Ok(st.chars().next().unwrap()); + } + if bytes.len() >= 4 { + return error; } - if bytes.len() >= 4 { return error; } } } } diff --git a/src/input.rs b/src/input.rs index a06a411..b38b70d 100644 --- a/src/input.rs +++ b/src/input.rs @@ -3,7 +3,7 @@ use std::io::{self, Read, Write}; use std::ops; -use event::{parse_event, Event, Key}; +use event::{self, Event, Key}; use raw::IntoRawMode; /// An iterator over input keys. @@ -41,7 +41,7 @@ impl Iterator for Events { if let Some(c) = self.leftover { // we have a leftover byte, use it self.leftover = None; - return Some(parse_event(Ok(c), &mut source.bytes()).or(Ok(Event::Unsupported))); + return Some(parse_event(c, &mut source.bytes())); } // Here we read two bytes at a time. We need to distinguish between single ESC key presses, @@ -51,15 +51,17 @@ impl Iterator for Events { let mut buf = [0u8; 2]; let res = match source.read(&mut buf) { Ok(0) => return None, - Ok(1) => match buf[0] { - b'\x1B' => Ok(Event::Key(Key::Esc)), - c => parse_event(Ok(c), &mut source.bytes()), - }, + Ok(1) => { + match buf[0] { + b'\x1B' => Ok(Event::Key(Key::Esc)), + c => parse_event(c, &mut source.bytes()), + } + } Ok(2) => { let mut option_iter = &mut Some(buf[1]).into_iter(); let result = { let mut iter = option_iter.map(|c| Ok(c)).chain(source.bytes()); - parse_event(Ok(buf[0]), &mut iter) + parse_event(buf[0], &mut iter) }; // If the option_iter wasn't consumed, keep the byte for later. self.leftover = option_iter.next(); @@ -69,10 +71,24 @@ impl Iterator for Events { Err(e) => Err(e), }; - Some(res.or(Ok(Event::Unsupported))) + Some(res) } } +fn parse_event(item: u8, iter: &mut I) -> Result + where I: Iterator> +{ + let mut buf = vec![item]; + let result = { + let mut iter = iter.inspect(|byte| if let &Ok(byte) = byte { + buf.push(byte); + }); + event::parse_event(item, &mut iter) + }; + result.or(Ok(Event::Unsupported(buf))) +} + + /// Extension to `Read` trait. pub trait TermRead { /// An iterator over input events. @@ -105,9 +121,7 @@ impl TermRead for R { } } fn keys(self) -> Keys { - Keys { - iter: self.events(), - } + Keys { iter: self.events() } } fn read_line(&mut self) -> io::Result> { @@ -117,13 +131,16 @@ impl TermRead for R { match c { Err(e) => return Err(e), Ok(0) | Ok(3) | Ok(4) => return Ok(None), - Ok(0x7f) => { buf.pop(); }, + Ok(0x7f) => { + buf.pop(); + } Ok(b'\n') | Ok(b'\r') => break, Ok(c) => buf.push(c), } } - let string = try!(String::from_utf8(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))); + let string = try!(String::from_utf8(buf) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))); Ok(Some(string)) } } @@ -200,18 +217,25 @@ mod test { #[test] fn test_events() { let mut i = b"\x1B[\x00bc\x7F\x1B[D\ - \x1B[M\x00\x22\x24\x1B[<0;2;4;M\x1B[32;2;4M\x1B[<0;2;4;m\x1B[35;2;4Mb".events(); + \x1B[M\x00\x22\x24\x1B[<0;2;4;M\x1B[32;2;4M\x1B[<0;2;4;m\x1B[35;2;4Mb" + .events(); - assert_eq!(i.next().unwrap().unwrap(), Event::Unsupported); + assert_eq!(i.next().unwrap().unwrap(), + Event::Unsupported(vec![0x1B, b'[', 0x00])); assert_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Char('b'))); assert_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Char('c'))); assert_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Backspace)); assert_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Left)); - assert_eq!(i.next().unwrap().unwrap(), Event::Mouse(MouseEvent::Press(MouseButton::WheelUp, 2, 4))); - assert_eq!(i.next().unwrap().unwrap(), Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4))); - assert_eq!(i.next().unwrap().unwrap(), Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4))); - assert_eq!(i.next().unwrap().unwrap(), Event::Mouse(MouseEvent::Release(2, 4))); - assert_eq!(i.next().unwrap().unwrap(), Event::Mouse(MouseEvent::Release(2, 4))); + assert_eq!(i.next().unwrap().unwrap(), + Event::Mouse(MouseEvent::Press(MouseButton::WheelUp, 2, 4))); + assert_eq!(i.next().unwrap().unwrap(), + Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4))); + assert_eq!(i.next().unwrap().unwrap(), + Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4))); + assert_eq!(i.next().unwrap().unwrap(), + Event::Mouse(MouseEvent::Release(2, 4))); + assert_eq!(i.next().unwrap().unwrap(), + Event::Mouse(MouseEvent::Release(2, 4))); assert_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Char('b'))); assert!(i.next().is_none()); } @@ -219,13 +243,14 @@ mod test { #[test] fn test_function_keys() { let mut st = b"\x1BOP\x1BOQ\x1BOR\x1BOS".keys(); - for i in 1 .. 5 { + for i in 1..5 { assert_eq!(st.next().unwrap().unwrap(), Key::F(i)); } let mut st = b"\x1B[11~\x1B[12~\x1B[13~\x1B[14~\x1B[15~\ - \x1B[17~\x1B[18~\x1B[19~\x1B[20~\x1B[21~\x1B[23~\x1B[24~".keys(); - for i in 1 .. 13 { + \x1B[17~\x1B[18~\x1B[19~\x1B[20~\x1B[21~\x1B[23~\x1B[24~" + .keys(); + for i in 1..13 { assert_eq!(st.next().unwrap().unwrap(), Key::F(i)); } } @@ -279,14 +304,18 @@ mod test { #[test] fn test_backspace() { - line_match("this is the\x7f first\x7f\x7f test", Some("this is th fir test")); - line_match("this is the seco\x7fnd test\x7f", Some("this is the secnd tes")); + line_match("this is the\x7f first\x7f\x7f test", + Some("this is th fir test")); + line_match("this is the seco\x7fnd test\x7f", + Some("this is the secnd tes")); } #[test] fn test_end() { - line_match("abc\nhttps://www.youtube.com/watch?v=dQw4w9WgXcQ", Some("abc")); - line_match("hello\rhttps://www.youtube.com/watch?v=yPYZpwSpKmA", Some("hello")); + line_match("abc\nhttps://www.youtube.com/watch?v=dQw4w9WgXcQ", + Some("abc")); + line_match("hello\rhttps://www.youtube.com/watch?v=yPYZpwSpKmA", + Some("hello")); } #[test]