From 00ca758d45b546fb167403e286be82c4ff5eedb7 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Fri, 16 Dec 2016 18:20:18 +0100 Subject: [PATCH 1/2] Add `UnknownCSI` event type (#73) * Add `UnknownCSI` event type. * Compile test from `raw` module But don't run it. * Fix unused import warning in doc-test * Rename UnknownCSI -> UnknownCsi --- src/color.rs | 4 +--- src/event.rs | 37 ++++++++++++++++++++++++++----------- src/raw.rs | 2 +- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/color.rs b/src/color.rs index e154ae2..c1eee52 100644 --- a/src/color.rs +++ b/src/color.rs @@ -3,9 +3,7 @@ //! # Example //! //! ```rust -//! use termion::{color, style}; -//! -//! use std::io; +//! use termion::color; //! //! fn main() { //! println!("{}Red", color::Fg(color::Red)); diff --git a/src/event.rs b/src/event.rs index 9d338c6..44a139e 100644 --- a/src/event.rs +++ b/src/event.rs @@ -5,7 +5,7 @@ use std::ascii::AsciiExt; use std::str; /// An event reported by the terminal. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Event { /// A key press. Key(Key), @@ -13,6 +13,9 @@ pub enum Event { Mouse(MouseEvent), /// An event that cannot currently be evaluated. Unsupported, + /// A CSI sequence unrecognized by termion. Does not inlude the leading `^[`. + UnknownCsi(Vec), + } /// A mouse related event. @@ -93,7 +96,6 @@ pub enum Key { /// Esc key. Esc, - #[allow(missing_docs)] #[doc(hidden)] __IsNotComplete } @@ -108,11 +110,13 @@ where I: Iterator> 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(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), @@ -198,17 +202,24 @@ where I: Iterator> 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 = &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 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), @@ -218,7 +229,7 @@ where I: Iterator> 64 => MouseEvent::Hold(cx, cy), 96 | 97 => MouseEvent::Press(MouseButton::WheelUp, cx, cy), - _ => return error, + _ => return Ok(Event::UnknownCsi(str_buf.into_bytes())), }; Event::Mouse(event) @@ -229,16 +240,20 @@ where I: Iterator> // This CSI sequence can be a list of semicolon-separated // numbers. - let nums: Vec = str_buf.split(';') + let nums: Vec = str_buf[..str_buf.len()-1].split(';') .map(|n| n.parse().unwrap()) .collect(); if nums.is_empty() { - return error; + 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), @@ -249,10 +264,10 @@ where I: Iterator> 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 error, + _ => return Ok(Event::UnknownCsi(str_buf.into_bytes())), } }, - _ => return error, + _ => return Ok(Event::UnknownCsi(buf)), } }, _ => return error, diff --git a/src/raw.rs b/src/raw.rs index 20a07f9..09cd5e4 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -11,7 +11,7 @@ //! //! # Example //! -//! ```rust,ignore +//! ```rust,no_run //! use termion::raw::IntoRawMode; //! use std::io::{Write, stdout}; //! From c2df3d7edd01566c1db3c110615df27c210726be Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Sun, 18 Dec 2016 00:49:40 +0100 Subject: [PATCH 2/2] Discard leftover input when consumed (#76) * Discard leftover input when consumed The iterator given to `parse_event` now `take()` the value from `leftover`. * Always save leftover `parse_event` will consume it when required. * Remove OptionIterator, directly uses std::option::Iter --- src/input.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/input.rs b/src/input.rs index 26abd3e..a06a411 100644 --- a/src/input.rs +++ b/src/input.rs @@ -56,14 +56,14 @@ impl Iterator for Events { c => parse_event(Ok(c), &mut source.bytes()), }, Ok(2) => { - if buf[0] != b'\x1B' { - // this is not an escape sequence, but we read two bytes, save the second byte - // for later - self.leftover = Some(buf[1]); - } - - let mut iter = buf[1..2].iter().map(|c| Ok(*c)).chain(source.bytes()); - parse_event(Ok(buf[0]), &mut iter) + 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) + }; + // If the option_iter wasn't consumed, keep the byte for later. + self.leftover = option_iter.next(); + result } Ok(_) => unreachable!(), Err(e) => Err(e),