From ea06c6fd5672bae7918c7c9925410b2c143b03f0 Mon Sep 17 00:00:00 2001 From: Matthew Nicholson Date: Wed, 26 Oct 2016 05:53:36 -0400 Subject: [PATCH] Modify Keys and Events to detect Esc key presses (#45) * modify Keys and Events to detect Esc key presses The strategy used here is to read two bytes at a time, going on the assumption that escape sequences will consist of multi byte reads and solitary Esc key presses will consist of single byte reads. Tests had to be modified to account for these new multi byte reads by including dummy bytes when a single byte was previously expected. Fixes ticki/termion#43 * expand keys example to include Esc key presses * add test for Esc key press --- examples/keys.rs | 1 + src/event.rs | 2 ++ src/input.rs | 68 +++++++++++++++++++++++++++++++++++++----------- 3 files changed, 56 insertions(+), 15 deletions(-) diff --git a/examples/keys.rs b/examples/keys.rs index a67f931..1b44bc7 100644 --- a/examples/keys.rs +++ b/examples/keys.rs @@ -23,6 +23,7 @@ fn main() { Key::Char(c) => println!("{}", c), Key::Alt(c) => println!("^{}", c), Key::Ctrl(c) => println!("*{}", c), + Key::Esc => println!("ESC"), Key::Left => println!("←"), Key::Right => println!("→"), Key::Up => println!("↑"), diff --git a/src/event.rs b/src/event.rs index 5760228..7d0a970 100644 --- a/src/event.rs +++ b/src/event.rs @@ -90,6 +90,8 @@ pub enum Key { Ctrl(char), /// Null byte. Null, + /// Esc key. + Esc, #[allow(missing_docs)] #[doc(hidden)] diff --git a/src/input.rs b/src/input.rs index 6d57cdb..26abd3e 100644 --- a/src/input.rs +++ b/src/input.rs @@ -7,11 +7,11 @@ use event::{parse_event, Event, Key}; use raw::IntoRawMode; /// An iterator over input keys. -pub struct Keys { - iter: Events, +pub struct Keys { + iter: Events, } -impl>> Iterator for Keys { +impl Iterator for Keys { type Item = Result; fn next(&mut self) -> Option> { @@ -27,29 +27,59 @@ impl>> Iterator for Keys { } /// An iterator over input events. -pub struct Events { - bytes: I, +pub struct Events { + source: R, + leftover: Option, } -impl>> Iterator for Events { +impl Iterator for Events { type Item = Result; fn next(&mut self) -> Option> { - let iter = &mut self.bytes; - match iter.next() { - Some(item) => Some(parse_event(item, iter).or(Ok(Event::Unsupported))), - None => None, + let mut source = &mut self.source; + + 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))); } + + // Here we read two bytes at a time. We need to distinguish between single ESC key presses, + // and escape sequences (which start with ESC or a x1B byte). The idea is that if this is + // an escape sequence, we will read multiple bytes (the first byte being ESC) but if this + // is a single ESC keypress, we will only read a single byte. + 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(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) + } + Ok(_) => unreachable!(), + Err(e) => Err(e), + }; + + Some(res.or(Ok(Event::Unsupported))) } } /// 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. /// @@ -68,12 +98,13 @@ pub trait TermRead { } impl TermRead for R { - fn events(self) -> Events> { + fn events(self) -> Events { Events { - bytes: self.bytes(), + source: self, + leftover: None, } } - fn keys(self) -> Keys> { + fn keys(self) -> Keys { Keys { iter: self.events(), } @@ -213,6 +244,13 @@ mod test { assert!(st.next().is_none()); } + #[test] + fn test_esc_key() { + let mut st = b"\x1B".keys(); + assert_eq!(st.next().unwrap().unwrap(), Key::Esc); + assert!(st.next().is_none()); + } + fn line_match(a: &str, b: Option<&str>) { let mut sink = io::sink();