From a7624ccb8cd4254065454a30735dded74fc830bd Mon Sep 17 00:00:00 2001 From: jocutajar Date: Sat, 26 Jan 2019 12:17:09 +0100 Subject: [PATCH 01/10] Autoformat --- src/event.rs | 332 +++++++++++++++++++++++++-------------------------- 1 file changed, 163 insertions(+), 169 deletions(-) diff --git a/src/event.rs b/src/event.rs index 6e383a1..e1c558f 100644 --- a/src/event.rs +++ b/src/event.rs @@ -98,30 +98,31 @@ pub enum Key { /// Parse an Event from `item` and possibly subsequent bytes through `iter`. pub fn parse_event(item: u8, iter: &mut I) -> Result - where I: Iterator> +where + I: Iterator>, { let error = Error::new(ErrorKind::Other, "Could not parse an event"); match item { 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 Err(error), + 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 Err(error), + } } - } - Some(Ok(b'[')) => { - // This is a CSI sequence. - 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 Err(error), - }) + Some(Ok(b'[')) => { + // This is a CSI sequence. + 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 Err(error), + }) } b'\n' | b'\r' => Ok(Event::Key(Key::Char('\n'))), b'\t' => Ok(Event::Key(Key::Char('\t'))), @@ -129,12 +130,10 @@ pub fn parse_event(item: u8, iter: &mut I) -> Result 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))) - }) - } + c => Ok({ + let ch = parse_utf8_char(c, iter); + Event::Key(Key::Char(try!(ch))) + }), } } @@ -142,178 +141,173 @@ pub fn parse_event(item: u8, iter: &mut I) -> Result /// /// Returns None if an unrecognized sequence is found. fn parse_csi(iter: &mut I) -> Option - where I: Iterator> +where + I: Iterator>, { Some(match iter.next() { - Some(Ok(b'[')) => match iter.next() { - Some(Ok(val @ b'A'...b'E')) => Event::Key(Key::F(1 + val - b'A')), - _ => return None, - }, - 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(); + Some(Ok(b'[')) => match iter.next() { + Some(Ok(val @ b'A'...b'E')) => Event::Key(Key::F(1 + val - b'A')), + _ => return None, + }, + 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 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, + }) } - let str_buf = String::from_utf8(buf).unwrap(); - let nums = &mut str_buf.split(';'); + 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 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, + 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), - 3 => MouseEvent::Release(cx, cy), - _ => return None, - }; + 32 => MouseEvent::Hold(cx, cy), + 3 => MouseEvent::Release(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(); + 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(); + 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 nums: Vec = str_buf.split(';').map(|n| n.parse().unwrap()).collect(); - let cb = nums[0]; - let cx = nums[1]; - let cy = nums[2]; + 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, - }; + 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(); + 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(); + // 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; - } + 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; - } + // 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, + 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, } - } - _ => 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")); + let error = Err(Error::new( + ErrorKind::Other, + "Input character is not valid UTF-8", + )); if c.is_ascii() { Ok(c as char) } else { -- GitLab From a860d616243fa968c1e37287b517259761a776c3 Mon Sep 17 00:00:00 2001 From: jocutajar Date: Sat, 26 Jan 2019 13:33:14 +0100 Subject: [PATCH 02/10] Return unrecognised SCI sequences as unsupported events --- src/event.rs | 68 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 20 deletions(-) diff --git a/src/event.rs b/src/event.rs index e1c558f..ca2fae7 100644 --- a/src/event.rs +++ b/src/event.rs @@ -101,7 +101,6 @@ pub fn parse_event(item: u8, iter: &mut I) -> Result where I: Iterator>, { - let error = Error::new(ErrorKind::Other, "Could not parse an event"); match item { b'\x1B' => { // This is an escape character, leading a control sequence. @@ -110,18 +109,21 @@ where match iter.next() { // F1-F4 Some(Ok(val @ b'P'...b'S')) => Event::Key(Key::F(1 + val - b'P')), - _ => return Err(error), + Some(Ok(val)) => Event::Unsupported(vec![b'\x1B', b'0', val]), + Some(Err(e)) => return Err(e), + None => Event::Unsupported(vec![b'\x1B', b'0']), } } Some(Ok(b'[')) => { // This is a CSI sequence. - parse_csi(iter).ok_or(error)? + parse_csi(iter)? } Some(Ok(c)) => { let ch = parse_utf8_char(c, iter); Event::Key(Key::Alt(try!(ch))) } - Some(Err(_)) | None => return Err(error), + Some(Err(e)) => return Err(e), + None => Event::Unsupported(vec![b'\x1B']), }) } b'\n' | b'\r' => Ok(Event::Key(Key::Char('\n'))), @@ -139,15 +141,17 @@ where /// Parses a CSI sequence, just after reading ^[ /// -/// Returns None if an unrecognized sequence is found. -fn parse_csi(iter: &mut I) -> Option +/// Returns Ok(Event::Unsupported) if an unrecognized sequence is found. +fn parse_csi(iter: &mut I) -> Result where I: Iterator>, { - Some(match iter.next() { + Ok(match iter.next() { Some(Ok(b'[')) => match iter.next() { + None => unsupported_csi(&vec![b'[']), Some(Ok(val @ b'A'...b'E')) => Event::Key(Key::F(1 + val - b'A')), - _ => return None, + Some(Ok(val)) => unsupported_csi(&vec![b'[', val]), + Some(Err(e)) => return Err(e), }, Some(Ok(b'D')) => Event::Key(Key::Left), Some(Ok(b'C')) => Event::Key(Key::Right), @@ -159,10 +163,14 @@ where // X10 emulation mouse encoding: ESC [ CB Cx Cy (6 characters only). let mut next = || iter.next().unwrap().unwrap(); - let cb = next() as i8 - 32; + let b1 = next(); + let b2 = next(); + let b3 = next(); + + let cb = b1 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; + let cx = b2.saturating_sub(32) as u16; + let cy = b3.saturating_sub(32) as u16; Event::Mouse(match cb & 0b11 { 0 => { if cb & 0x40 != 0 { @@ -180,7 +188,7 @@ where } 2 => MouseEvent::Press(MouseButton::Right, cx, cy), 3 => MouseEvent::Release(cx, cy), - _ => return None, + _ => return Ok(unsupported_csi(&vec![b'M', b1, b2, b3])), }) } Some(Ok(b'<')) => { @@ -215,12 +223,12 @@ where match c { b'M' => MouseEvent::Press(button, cx, cy), b'm' => MouseEvent::Release(cx, cy), - _ => return None, + _ => return Ok(unsupported_csi(str_buf.as_bytes())), } } 32 => MouseEvent::Hold(cx, cy), 3 => MouseEvent::Release(cx, cy), - _ => return None, + _ => return Ok(unsupported_csi(str_buf.as_bytes())), }; Event::Mouse(event) @@ -256,7 +264,9 @@ where 35 => MouseEvent::Release(cx, cy), 64 => MouseEvent::Hold(cx, cy), 96 | 97 => MouseEvent::Press(MouseButton::WheelUp, cx, cy), - _ => return None, + _ => { + return Ok(unsupported_csi(str_buf.as_bytes())); + } }; Event::Mouse(event) @@ -270,13 +280,13 @@ where let nums: Vec = str_buf.split(';').map(|n| n.parse().unwrap()).collect(); if nums.is_empty() { - return None; + return Ok(unsupported_csi(str_buf.as_bytes())); } // TODO: handle multiple values for key modififiers (ex: values // [3, 2] means Shift+Delete) if nums.len() > 1 { - return None; + return Ok(unsupported_csi(str_buf.as_bytes())); } match nums[0] { @@ -289,16 +299,24 @@ where 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 Ok(unsupported_csi(str_buf.as_bytes())), } } - _ => return None, + _ => return Ok(unsupported_csi(&buf)), } } - _ => return None, + Some(Ok(c)) => return Ok(unsupported_csi(&vec![c])), + Some(Err(e)) => return Err(e), + None => return Ok(unsupported_csi(&vec![])), }) } +fn unsupported_csi(bytes: &[u8]) -> Event { + let mut seq = vec![b'\x1B', b'[']; + seq.extend(bytes); + Event::Unsupported(seq) +} + /// 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 @@ -342,3 +360,13 @@ fn test_parse_utf8() { assert!(c == parse_utf8_char(b, bytes).unwrap()); } } + +#[test] +fn test_parse_invalid_mouse() { + let item = b'\x1B'; + let mut iter = "[x".bytes().map(|x| Ok(x)); + assert_eq!( + parse_event(item, &mut iter).unwrap(), + Event::Unsupported(vec![b'\x1B', b'[', b'x']) + ) +} -- GitLab From e53e408d4af59ab036bfecc950b1c09d3dbe5f1a Mon Sep 17 00:00:00 2001 From: jocutajar Date: Sat, 26 Jan 2019 13:45:58 +0100 Subject: [PATCH 03/10] Eliminate unwraps --- src/event.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/event.rs b/src/event.rs index ca2fae7..7c10c75 100644 --- a/src/event.rs +++ b/src/event.rs @@ -233,16 +233,19 @@ where Event::Mouse(event) } - Some(Ok(c @ b'0'...b'9')) => { + Some(Ok(mut 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(); + while let Some(n) = iter.next() { + c = n?; + if c < 64 || c > 126 { + buf.push(c); + } else { + break; + } } match c { -- GitLab From 55855fdb2b85174872152e4c8ed2f0b97c77072d Mon Sep 17 00:00:00 2001 From: jocutajar Date: Sun, 27 Jan 2019 18:19:56 +0100 Subject: [PATCH 04/10] Eliminate unwraps, tidy up --- src/event.rs | 139 +++++++++++++++++++++++++++++++-------------------- src/input.rs | 16 +----- 2 files changed, 87 insertions(+), 68 deletions(-) diff --git a/src/event.rs b/src/event.rs index 7c10c75..230d2b5 100644 --- a/src/event.rs +++ b/src/event.rs @@ -96,8 +96,26 @@ pub enum Key { __IsNotComplete, } +pub fn parse_event(item: u8, iter: &mut I) -> Result<(Event, Vec), Error> +where + I: Iterator>, +{ + let mut buf = vec![item]; + let result = { + let mut iter = iter.inspect(|byte| { + if let &Ok(byte) = byte { + buf.push(byte); + } + }); + try_parse_event(item, &mut iter) + }; + result + .or(Ok(Event::Unsupported(buf.clone()))) + .map(|e| (e, buf)) +} + /// Parse an Event from `item` and possibly subsequent bytes through `iter`. -pub fn parse_event(item: u8, iter: &mut I) -> Result +fn try_parse_event(item: u8, iter: &mut I) -> Result where I: Iterator>, { @@ -139,6 +157,20 @@ where } } +fn pop(iter: &mut I) -> Result +where + I: Iterator>, +{ + iter.next().unwrap_or(Err(err_unexpected_eof())) +} + +fn err_invalid_input() -> Error { + Error::from(ErrorKind::InvalidInput) +} +fn err_unexpected_eof() -> Error { + Error::from(ErrorKind::UnexpectedEof) +} + /// Parses a CSI sequence, just after reading ^[ /// /// Returns Ok(Event::Unsupported) if an unrecognized sequence is found. @@ -146,26 +178,25 @@ fn parse_csi(iter: &mut I) -> Result where I: Iterator>, { - Ok(match iter.next() { - Some(Ok(b'[')) => match iter.next() { - None => unsupported_csi(&vec![b'[']), + Ok(match pop(iter)? { + b'[' => match iter.next() { + None => return Err(err_unexpected_eof()), Some(Ok(val @ b'A'...b'E')) => Event::Key(Key::F(1 + val - b'A')), - Some(Ok(val)) => unsupported_csi(&vec![b'[', val]), + Some(Ok(_)) => return Err(err_invalid_input()), Some(Err(e)) => return Err(e), }, - 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')) => { + b'D' => Event::Key(Key::Left), + b'C' => Event::Key(Key::Right), + b'A' => Event::Key(Key::Up), + b'B' => Event::Key(Key::Down), + b'H' => Event::Key(Key::Home), + b'F' => Event::Key(Key::End), + b'M' => { // X10 emulation mouse encoding: ESC [ CB Cx Cy (6 characters only). - let mut next = || iter.next().unwrap().unwrap(); - let b1 = next(); - let b2 = next(); - let b3 = next(); + let b1 = pop(iter)?; + let b2 = pop(iter)?; + let b3 = pop(iter)?; let cb = b1 as i8 - 32; // (1, 1) are the coords for upper left. @@ -188,27 +219,29 @@ where } 2 => MouseEvent::Press(MouseButton::Right, cx, cy), 3 => MouseEvent::Release(cx, cy), - _ => return Ok(unsupported_csi(&vec![b'M', b1, b2, b3])), + _ => return Err(err_invalid_input()), }) } - Some(Ok(b'<')) => { + b'<' => { // xterm mouse encoding: // ESC [ < Cb ; Cx ; Cy (;) (M or m) let mut buf = Vec::new(); - let mut c = iter.next().unwrap().unwrap(); + let mut c = pop(iter)?; while match c { b'm' | b'M' => false, _ => true, } { buf.push(c); - c = iter.next().unwrap().unwrap(); + c = pop(iter)?; } - let str_buf = String::from_utf8(buf).unwrap(); - let nums = &mut str_buf.split(';'); + let str_buf = String::from_utf8(buf).map_err(|_| err_invalid_input())?; + let nums = &mut str_buf + .split(';') + .map(|n| n.parse::().map_err(|_| err_invalid_input())); - let cb = nums.next().unwrap().parse::().unwrap(); - let cx = nums.next().unwrap().parse::().unwrap(); - let cy = nums.next().unwrap().parse::().unwrap(); + let cb = pop(nums)?; + let cx = pop(nums)?; + let cy = pop(nums)?; let event = match cb { 0...2 | 64...65 => { @@ -223,17 +256,17 @@ where match c { b'M' => MouseEvent::Press(button, cx, cy), b'm' => MouseEvent::Release(cx, cy), - _ => return Ok(unsupported_csi(str_buf.as_bytes())), + _ => return Err(err_invalid_input()), } } 32 => MouseEvent::Hold(cx, cy), 3 => MouseEvent::Release(cx, cy), - _ => return Ok(unsupported_csi(str_buf.as_bytes())), + _ => return Err(err_invalid_input()), }; Event::Mouse(event) } - Some(Ok(mut c @ b'0'...b'9')) => { + mut c @ b'0'...b'9' => { // Numbered escape code. let mut buf = Vec::new(); buf.push(c); @@ -252,13 +285,15 @@ where // rxvt mouse encoding: // ESC [ Cb ; Cx ; Cy ; M b'M' => { - let str_buf = String::from_utf8(buf).unwrap(); + let str_buf = String::from_utf8(buf).map_err(|_| err_invalid_input())?; - let nums: Vec = str_buf.split(';').map(|n| n.parse().unwrap()).collect(); + let mut nums = str_buf + .split(';') + .map(|n| n.parse().map_err(|_| err_invalid_input())); - let cb = nums[0]; - let cx = nums[1]; - let cy = nums[2]; + let cb = pop(&mut nums)?; + let cx = pop(&mut nums)?; + let cy = pop(&mut nums)?; let event = match cb { 32 => MouseEvent::Press(MouseButton::Left, cx, cy), @@ -268,7 +303,7 @@ where 64 => MouseEvent::Hold(cx, cy), 96 | 97 => MouseEvent::Press(MouseButton::WheelUp, cx, cy), _ => { - return Ok(unsupported_csi(str_buf.as_bytes())); + return Err(err_invalid_input()); } }; @@ -276,23 +311,23 @@ where } // Special key code. b'~' => { - let str_buf = String::from_utf8(buf).unwrap(); + let str_buf = String::from_utf8(buf).map_err(|_| err_invalid_input())?; // This CSI sequence can be a list of semicolon-separated // numbers. - let nums: Vec = str_buf.split(';').map(|n| n.parse().unwrap()).collect(); + let mut nums = str_buf + .split(';') + .map(|n| n.parse().map_err(|_| err_invalid_input())); - if nums.is_empty() { - return Ok(unsupported_csi(str_buf.as_bytes())); - } + let num = pop(&mut nums)?; // TODO: handle multiple values for key modififiers (ex: values // [3, 2] means Shift+Delete) - if nums.len() > 1 { - return Ok(unsupported_csi(str_buf.as_bytes())); + if let Some(_) = nums.next() { + return Err(err_invalid_input()); } - match nums[0] { + match num { 1 | 7 => Event::Key(Key::Home), 2 => Event::Key(Key::Insert), 3 => Event::Key(Key::Delete), @@ -302,24 +337,16 @@ where 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(unsupported_csi(str_buf.as_bytes())), + _ => return Err(err_invalid_input()), } } - _ => return Ok(unsupported_csi(&buf)), + _ => return Err(err_invalid_input()), } } - Some(Ok(c)) => return Ok(unsupported_csi(&vec![c])), - Some(Err(e)) => return Err(e), - None => return Ok(unsupported_csi(&vec![])), + _ => return Err(err_invalid_input()), }) } -fn unsupported_csi(bytes: &[u8]) -> Event { - let mut seq = vec![b'\x1B', b'[']; - seq.extend(bytes); - Event::Unsupported(seq) -} - /// 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 @@ -340,6 +367,7 @@ where Some(Ok(next)) => { bytes.push(next); if let Ok(st) = str::from_utf8(bytes) { + // unwrap is safe here because parse was OK return Ok(st.chars().next().unwrap()); } if bytes.len() >= 4 { @@ -370,6 +398,9 @@ fn test_parse_invalid_mouse() { let mut iter = "[x".bytes().map(|x| Ok(x)); assert_eq!( parse_event(item, &mut iter).unwrap(), - Event::Unsupported(vec![b'\x1B', b'[', b'x']) + ( + Event::Unsupported(vec![b'\x1B', b'[', b'x']), + vec![b'\x1B', b'[', b'x'] + ) ) } diff --git a/src/input.rs b/src/input.rs index 5c8ecf4..1c7c4c1 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1,9 +1,10 @@ //! User input. +use event::parse_event; use std::io::{self, Read, Write}; use std::ops; -use event::{self, Event, Key}; +use event::{Event, Key}; use raw::IntoRawMode; /// An iterator over input keys. @@ -88,19 +89,6 @@ impl Iterator for EventsAndRaw { } } -fn parse_event(item: u8, iter: &mut I) -> Result<(Event, Vec), io::Error> - 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.clone()))).map(|e| (e, buf)) -} - /// Extension to `Read` trait. pub trait TermRead { -- GitLab From 81dfa51b262a4497f89359a083022ad38cced5de Mon Sep 17 00:00:00 2001 From: jocutajar Date: Sun, 27 Jan 2019 18:33:06 +0100 Subject: [PATCH 05/10] log event parsing errors --- Cargo.toml | 1 + src/event.rs | 5 ++++- src/lib.rs | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6e88e81..f95b428 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ exclude = ["target", "CHANGELOG.md", "image.png", "Cargo.lock"] [dependencies] numtoa = { version = "0.1.0", features = ["std"]} +log = "0.4" [target.'cfg(not(target_os = "redox"))'.dependencies] libc = "0.2.8" diff --git a/src/event.rs b/src/event.rs index 230d2b5..c0d1132 100644 --- a/src/event.rs +++ b/src/event.rs @@ -110,7 +110,10 @@ where try_parse_event(item, &mut iter) }; result - .or(Ok(Event::Unsupported(buf.clone()))) + .or_else(|err| { + warn!("Event parse error: {}", err); + Ok(Event::Unsupported(buf.clone())) + }) .map(|e| (e, buf)) } diff --git a/src/lib.rs b/src/lib.rs index 1d8f66f..f2782d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,9 @@ extern crate numtoa; +#[macro_use] +extern crate log; + #[cfg(target_os = "redox")] #[path="sys/redox/mod.rs"] mod sys; -- GitLab From 85a3b19a9c19de7d774e8809f7180b7ad1193552 Mon Sep 17 00:00:00 2001 From: jocutajar Date: Sun, 27 Jan 2019 18:38:14 +0100 Subject: [PATCH 06/10] debug events --- src/event.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/event.rs b/src/event.rs index c0d1132..9133e5c 100644 --- a/src/event.rs +++ b/src/event.rs @@ -114,7 +114,10 @@ where warn!("Event parse error: {}", err); Ok(Event::Unsupported(buf.clone())) }) - .map(|e| (e, buf)) + .map(|e| { + debug!("Event: {:?}", e); + (e, buf) + }) } /// Parse an Event from `item` and possibly subsequent bytes through `iter`. -- GitLab From 19255a221f20c7ff97e33ec929799b59eb261a3f Mon Sep 17 00:00:00 2001 From: jocutajar Date: Sun, 27 Jan 2019 18:48:29 +0100 Subject: [PATCH 07/10] Try debug sudden death --- src/async.rs | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/async.rs b/src/async.rs index ea02489..c3214ed 100644 --- a/src/async.rs +++ b/src/async.rs @@ -11,16 +11,23 @@ use sys::tty::get_tty; pub fn async_stdin_until(delimiter: u8) -> AsyncReader { let (send, recv) = mpsc::channel(); - thread::spawn(move || for i in get_tty().unwrap().bytes() { - - match i { - Ok(byte) => { - let end_of_stream = &byte == &delimiter; - let send_error = send.send(Ok(byte)).is_err(); - - if end_of_stream || send_error { return; } - }, - Err(_) => { return; } + thread::spawn(move || { + for i in get_tty().unwrap().bytes() { + match i { + Ok(byte) => { + let end_of_stream = &byte == &delimiter; + let send_error = send.send(Ok(byte)).is_err(); + + if end_of_stream || send_error { + warn!("Send error"); + return; + } + } + Err(e) => { + warn!("Read error: {}", e); + return; + } + } } }); @@ -40,11 +47,13 @@ pub fn async_stdin_until(delimiter: u8) -> AsyncReader { pub fn async_stdin() -> AsyncReader { let (send, recv) = mpsc::channel(); - thread::spawn(move || for i in get_tty().unwrap().bytes() { - if send.send(i).is_err() { - return; - } - }); + thread::spawn(move || { + for i in get_tty().unwrap().bytes() { + if send.send(i).is_err() { + return; + } + } + }); AsyncReader { recv: recv } } @@ -80,7 +89,10 @@ impl Read for AsyncReader { total += 1; } Ok(Err(e)) => return Err(e), - Err(_) => break, + Err(e) => { + warn!("Receive error {}", e); + break; + } } } -- GitLab From 1686c32d57310f5e728930ffc521c451fe97081c Mon Sep 17 00:00:00 2001 From: jocutajar Date: Sun, 27 Jan 2019 18:56:11 +0100 Subject: [PATCH 08/10] Handle async receiver disconnect as a read error --- src/async.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/async.rs b/src/async.rs index c3214ed..df80638 100644 --- a/src/async.rs +++ b/src/async.rs @@ -89,9 +89,10 @@ impl Read for AsyncReader { total += 1; } Ok(Err(e)) => return Err(e), - Err(e) => { - warn!("Receive error {}", e); - break; + Err(mpsc::TryRecvError::Empty) => break, + Err(mpsc::TryRecvError::Disconnected) => { + warn!("Receiver disconnected"); + return Err(io::Error::from(io::ErrorKind::BrokenPipe)); } } } -- GitLab From b720cfed2cfa0205f15b574757b794b64603d9b1 Mon Sep 17 00:00:00 2001 From: jocutajar Date: Sun, 27 Jan 2019 19:08:50 +0100 Subject: [PATCH 09/10] autoformat --- src/input.rs | 152 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 94 insertions(+), 58 deletions(-) diff --git a/src/input.rs b/src/input.rs index 1c7c4c1..d322e8b 100644 --- a/src/input.rs +++ b/src/input.rs @@ -28,15 +28,17 @@ impl Iterator for Keys { } /// An iterator over input events. -pub struct Events { - inner: EventsAndRaw +pub struct Events { + inner: EventsAndRaw, } impl Iterator for Events { type Item = Result; fn next(&mut self) -> Option> { - self.inner.next().map(|tuple| tuple.map(|(event, _raw)| event)) + self.inner + .next() + .map(|tuple| tuple.map(|(event, _raw)| event)) } } @@ -65,12 +67,10 @@ impl Iterator for EventsAndRaw { 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), vec![b'\x1B'])), - c => parse_event(c, &mut source.bytes()), - } - } + Ok(1) => match buf[0] { + b'\x1B' => Ok((Event::Key(Key::Esc), vec![b'\x1B'])), + c => parse_event(c, &mut source.bytes()), + }, Ok(2) => { let mut option_iter = &mut Some(buf[1]).into_iter(); let result = { @@ -89,14 +89,17 @@ impl Iterator for EventsAndRaw { } } - /// Extension to `Read` trait. pub trait TermRead { /// An iterator over input events. - fn events(self) -> Events where Self: Sized; + fn events(self) -> Events + where + Self: Sized; /// An iterator over key inputs. - fn keys(self) -> Keys where Self: Sized; + fn keys(self) -> Keys + where + Self: Sized; /// Read a line. /// @@ -114,15 +117,16 @@ pub trait TermRead { } } - impl TermRead for R { fn events(self) -> Events { Events { - inner: self.events_and_raw() + inner: self.events_and_raw(), } } fn keys(self) -> Keys { - Keys { iter: self.events() } + Keys { + iter: self.events(), + } } fn read_line(&mut self) -> io::Result> { @@ -140,8 +144,8 @@ impl TermRead for R { } } - 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)) } } @@ -149,7 +153,9 @@ impl TermRead for R { /// Extension to `TermRead` trait. A separate trait in order to maintain backwards compatibility. pub trait TermReadEventsAndRaw { /// An iterator over input events and the bytes that define them. - fn events_and_raw(self) -> EventsAndRaw where Self: Sized; + fn events_and_raw(self) -> EventsAndRaw + where + Self: Sized; } impl TermReadEventsAndRaw for R { @@ -215,8 +221,8 @@ impl Write for MouseTerminal { #[cfg(test)] mod test { use super::*; + use event::{Event, Key, MouseButton, MouseEvent}; use std::io; - use event::{Key, Event, MouseEvent, MouseButton}; #[test] fn test_keys() { @@ -232,27 +238,38 @@ mod test { #[test] fn test_events() { - let mut i = - b"\x1B[\x00bc\x7F\x1B[D\ + 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(); + .events(); - assert_eq!(i.next().unwrap().unwrap(), - Event::Unsupported(vec![0x1B, b'[', 0x00])); + 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()); } @@ -263,25 +280,36 @@ mod test { \x1B[M\x00\x22\x24\x1B[<0;2;4;M\x1B[32;2;4M\x1B[<0;2;4;m\x1B[35;2;4Mb"; let mut output = Vec::::new(); { - let mut i = input.events_and_raw().map(|res| res.unwrap()) - .inspect(|&(_, ref raw)| { output.extend(raw); }).map(|(event, _)| event); - - assert_eq!(i.next().unwrap(), - Event::Unsupported(vec![0x1B, b'[', 0x00])); + let mut i = input + .events_and_raw() + .map(|res| res.unwrap()) + .inspect(|&(_, ref raw)| { + output.extend(raw); + }) + .map(|(event, _)| event); + + assert_eq!( + i.next().unwrap(), + Event::Unsupported(vec![0x1B, b'[', 0x00]) + ); assert_eq!(i.next().unwrap(), Event::Key(Key::Char('b'))); assert_eq!(i.next().unwrap(), Event::Key(Key::Char('c'))); assert_eq!(i.next().unwrap(), Event::Key(Key::Backspace)); assert_eq!(i.next().unwrap(), Event::Key(Key::Left)); - assert_eq!(i.next().unwrap(), - Event::Mouse(MouseEvent::Press(MouseButton::WheelUp, 2, 4))); - assert_eq!(i.next().unwrap(), - Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4))); - assert_eq!(i.next().unwrap(), - Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4))); - assert_eq!(i.next().unwrap(), - Event::Mouse(MouseEvent::Release(2, 4))); - assert_eq!(i.next().unwrap(), - Event::Mouse(MouseEvent::Release(2, 4))); + assert_eq!( + i.next().unwrap(), + Event::Mouse(MouseEvent::Press(MouseButton::WheelUp, 2, 4)) + ); + assert_eq!( + i.next().unwrap(), + Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4)) + ); + assert_eq!( + i.next().unwrap(), + Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4)) + ); + assert_eq!(i.next().unwrap(), Event::Mouse(MouseEvent::Release(2, 4))); + assert_eq!(i.next().unwrap(), Event::Mouse(MouseEvent::Release(2, 4))); assert_eq!(i.next().unwrap(), Event::Key(Key::Char('b'))); assert!(i.next().is_none()); } @@ -298,7 +326,7 @@ mod test { 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(); + .keys(); for i in 1..13 { assert_eq!(st.next().unwrap().unwrap(), Key::F(i)); } @@ -353,18 +381,26 @@ 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] -- GitLab From 0e82cf9ac60b9b0e435b7d835e9112a0811238aa Mon Sep 17 00:00:00 2001 From: jocutajar Date: Sun, 27 Jan 2019 19:10:25 +0100 Subject: [PATCH 10/10] log lone ESC and leftovers --- src/input.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/input.rs b/src/input.rs index d322e8b..cefa7e9 100644 --- a/src/input.rs +++ b/src/input.rs @@ -54,9 +54,9 @@ impl Iterator for EventsAndRaw { fn next(&mut self) -> Option), io::Error>> { let source = &mut self.source; - if let Some(c) = self.leftover { + if let Some(c) = self.leftover.take() { // we have a leftover byte, use it - self.leftover = None; + debug!("Leftover: {}", c); return Some(parse_event(c, &mut source.bytes())); } @@ -68,7 +68,10 @@ impl Iterator for EventsAndRaw { let res = match source.read(&mut buf) { Ok(0) => return None, Ok(1) => match buf[0] { - b'\x1B' => Ok((Event::Key(Key::Esc), vec![b'\x1B'])), + b'\x1B' => { + debug!("Event: lone ESC"); + Ok((Event::Key(Key::Esc), vec![b'\x1B'])) + } c => parse_event(c, &mut source.bytes()), }, Ok(2) => { -- GitLab