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
This commit is contained in:
parent
654db645cb
commit
ea06c6fd56
|
@ -23,6 +23,7 @@ fn main() {
|
||||||
Key::Char(c) => println!("{}", c),
|
Key::Char(c) => println!("{}", c),
|
||||||
Key::Alt(c) => println!("^{}", c),
|
Key::Alt(c) => println!("^{}", c),
|
||||||
Key::Ctrl(c) => println!("*{}", c),
|
Key::Ctrl(c) => println!("*{}", c),
|
||||||
|
Key::Esc => println!("ESC"),
|
||||||
Key::Left => println!("←"),
|
Key::Left => println!("←"),
|
||||||
Key::Right => println!("→"),
|
Key::Right => println!("→"),
|
||||||
Key::Up => println!("↑"),
|
Key::Up => println!("↑"),
|
||||||
|
|
|
@ -90,6 +90,8 @@ pub enum Key {
|
||||||
Ctrl(char),
|
Ctrl(char),
|
||||||
/// Null byte.
|
/// Null byte.
|
||||||
Null,
|
Null,
|
||||||
|
/// Esc key.
|
||||||
|
Esc,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
|
68
src/input.rs
68
src/input.rs
|
@ -7,11 +7,11 @@ use event::{parse_event, Event, Key};
|
||||||
use raw::IntoRawMode;
|
use raw::IntoRawMode;
|
||||||
|
|
||||||
/// An iterator over input keys.
|
/// An iterator over input keys.
|
||||||
pub struct Keys<I> {
|
pub struct Keys<R> {
|
||||||
iter: Events<I>,
|
iter: Events<R>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: Iterator<Item = Result<u8, io::Error>>> Iterator for Keys<I> {
|
impl<R: Read> Iterator for Keys<R> {
|
||||||
type Item = Result<Key, io::Error>;
|
type Item = Result<Key, io::Error>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Result<Key, io::Error>> {
|
fn next(&mut self) -> Option<Result<Key, io::Error>> {
|
||||||
|
@ -27,29 +27,59 @@ impl<I: Iterator<Item = Result<u8, io::Error>>> Iterator for Keys<I> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An iterator over input events.
|
/// An iterator over input events.
|
||||||
pub struct Events<I> {
|
pub struct Events<R> {
|
||||||
bytes: I,
|
source: R,
|
||||||
|
leftover: Option<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: Iterator<Item = Result<u8, io::Error>>> Iterator for Events<I> {
|
impl<R: Read> Iterator for Events<R> {
|
||||||
type Item = Result<Event, io::Error>;
|
type Item = Result<Event, io::Error>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Result<Event, io::Error>> {
|
fn next(&mut self) -> Option<Result<Event, io::Error>> {
|
||||||
let iter = &mut self.bytes;
|
let mut source = &mut self.source;
|
||||||
match iter.next() {
|
|
||||||
Some(item) => Some(parse_event(item, iter).or(Ok(Event::Unsupported))),
|
if let Some(c) = self.leftover {
|
||||||
None => None,
|
// 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.
|
/// Extension to `Read` trait.
|
||||||
pub trait TermRead {
|
pub trait TermRead {
|
||||||
/// An iterator over input events.
|
/// An iterator over input events.
|
||||||
fn events(self) -> Events<io::Bytes<Self>> where Self: Sized;
|
fn events(self) -> Events<Self> where Self: Sized;
|
||||||
|
|
||||||
/// An iterator over key inputs.
|
/// An iterator over key inputs.
|
||||||
fn keys(self) -> Keys<io::Bytes<Self>> where Self: Sized;
|
fn keys(self) -> Keys<Self> where Self: Sized;
|
||||||
|
|
||||||
/// Read a line.
|
/// Read a line.
|
||||||
///
|
///
|
||||||
|
@ -68,12 +98,13 @@ pub trait TermRead {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read> TermRead for R {
|
impl<R: Read> TermRead for R {
|
||||||
fn events(self) -> Events<io::Bytes<R>> {
|
fn events(self) -> Events<Self> {
|
||||||
Events {
|
Events {
|
||||||
bytes: self.bytes(),
|
source: self,
|
||||||
|
leftover: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn keys(self) -> Keys<io::Bytes<R>> {
|
fn keys(self) -> Keys<Self> {
|
||||||
Keys {
|
Keys {
|
||||||
iter: self.events(),
|
iter: self.events(),
|
||||||
}
|
}
|
||||||
|
@ -213,6 +244,13 @@ mod test {
|
||||||
assert!(st.next().is_none());
|
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>) {
|
fn line_match(a: &str, b: Option<&str>) {
|
||||||
let mut sink = io::sink();
|
let mut sink = io::sink();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue