Replace UnknownCsi with generalized Event::Unsupported (#80)
This commit is contained in:
parent
f9eaf6d0bc
commit
32ff8ba96e
367
src/event.rs
367
src/event.rs
|
@ -12,10 +12,7 @@ pub enum Event {
|
||||||
/// A mouse button press, release or wheel use at specific coordinates.
|
/// A mouse button press, release or wheel use at specific coordinates.
|
||||||
Mouse(MouseEvent),
|
Mouse(MouseEvent),
|
||||||
/// An event that cannot currently be evaluated.
|
/// An event that cannot currently be evaluated.
|
||||||
Unsupported,
|
Unsupported(Vec<u8>),
|
||||||
/// A CSI sequence unrecognized by termion. Does not inlude the leading `^[`.
|
|
||||||
UnknownCsi(Vec<u8>),
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A mouse related event.
|
/// A mouse related event.
|
||||||
|
@ -97,210 +94,218 @@ pub enum Key {
|
||||||
Esc,
|
Esc,
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
__IsNotComplete
|
__IsNotComplete,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an Event from `item` and possibly subsequent bytes through `iter`.
|
/// Parse an Event from `item` and possibly subsequent bytes through `iter`.
|
||||||
pub fn parse_event<I>(item: Result<u8, Error>, iter: &mut I) -> Result<Event, Error>
|
pub fn parse_event<I>(item: u8, iter: &mut I) -> Result<Event, Error>
|
||||||
where I: Iterator<Item = Result<u8, Error>>
|
where I: Iterator<Item = Result<u8, Error>>
|
||||||
{
|
{
|
||||||
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 {
|
match item {
|
||||||
Ok(b'\x1B') => {
|
b'\x1B' => {
|
||||||
|
// This is an escape character, leading a control sequence.
|
||||||
Ok(match iter.next() {
|
Ok(match iter.next() {
|
||||||
Some(Ok(b'O')) => {
|
Some(Ok(b'O')) => {
|
||||||
match iter.next() {
|
match iter.next() {
|
||||||
// F1-F4
|
// F1-F4
|
||||||
Some(Ok(val @ b'P' ... b'S')) => Event::Key(Key::F(1 + val - b'P')),
|
Some(Ok(val @ b'P'...b'S')) => Event::Key(Key::F(1 + val - b'P')),
|
||||||
_ => return error,
|
_ => return Err(error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(Ok(b'[')) => {
|
Some(Ok(b'[')) => {
|
||||||
// This is a CSI sequence.
|
// This is a CSI sequence.
|
||||||
match iter.next() {
|
parse_csi(iter).ok_or(error)?
|
||||||
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::<u16>().unwrap();
|
|
||||||
let cx = nums.next().unwrap().parse::<u16>().unwrap();
|
|
||||||
let cy = nums.next().unwrap().parse::<u16>().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<u16> = 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<u8> = 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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Some(Ok(c)) => {
|
Some(Ok(c)) => {
|
||||||
let ch = parse_utf8_char(c, iter);
|
let ch = parse_utf8_char(c, iter);
|
||||||
Event::Key(Key::Alt(try!(ch)))
|
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'))),
|
b'\n' | b'\r' => Ok(Event::Key(Key::Char('\n'))),
|
||||||
Ok(b'\t') => Ok(Event::Key(Key::Char('\t'))),
|
b'\t' => Ok(Event::Key(Key::Char('\t'))),
|
||||||
Ok(b'\x7F') => Ok(Event::Key(Key::Backspace)),
|
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))),
|
c @ b'\x01'...b'\x1A' => Ok(Event::Key(Key::Ctrl((c as u8 - 0x1 + b'a') as char))),
|
||||||
Ok(c @ b'\x1C'...b'\x1F') => {
|
c @ b'\x1C'...b'\x1F' => Ok(Event::Key(Key::Ctrl((c as u8 - 0x1C + b'4') as char))),
|
||||||
Ok(Event::Key(Key::Ctrl((c as u8 - 0x1C + b'4') as char)))
|
b'\0' => Ok(Event::Key(Key::Null)),
|
||||||
}
|
c => {
|
||||||
Ok(b'\0') => Ok(Event::Key(Key::Null)),
|
|
||||||
Ok(c) => {
|
|
||||||
Ok({
|
Ok({
|
||||||
let ch = parse_utf8_char(c, iter);
|
let ch = parse_utf8_char(c, iter);
|
||||||
Event::Key(Key::Char(try!(ch)))
|
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<I>(iter: &mut I) -> Option<Event>
|
||||||
|
where I: Iterator<Item = Result<u8, Error>>
|
||||||
|
{
|
||||||
|
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::<u16>().unwrap();
|
||||||
|
let cx = nums.next().unwrap().parse::<u16>().unwrap();
|
||||||
|
let cy = nums.next().unwrap().parse::<u16>().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<u16> = 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<u8> = 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.
|
/// Parse `c` as either a single byte ASCII char or a variable size UTF-8 char.
|
||||||
fn parse_utf8_char<I>(c: u8, iter: &mut I) -> Result<char, Error>
|
fn parse_utf8_char<I>(c: u8, iter: &mut I) -> Result<char, Error>
|
||||||
where I: Iterator<Item = Result<u8, Error>> {
|
where I: Iterator<Item = Result<u8, Error>>
|
||||||
|
{
|
||||||
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() {
|
if c.is_ascii() {
|
||||||
Ok(c as char)
|
Ok(c as char)
|
||||||
|
@ -311,9 +316,11 @@ fn parse_utf8_char<I>(c: u8, iter: &mut I) -> Result<char, Error>
|
||||||
loop {
|
loop {
|
||||||
bytes.push(iter.next().unwrap().unwrap());
|
bytes.push(iter.next().unwrap().unwrap());
|
||||||
if let Ok(st) = str::from_utf8(bytes) {
|
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; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
83
src/input.rs
83
src/input.rs
|
@ -3,7 +3,7 @@
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
use std::ops;
|
use std::ops;
|
||||||
|
|
||||||
use event::{parse_event, Event, Key};
|
use event::{self, Event, Key};
|
||||||
use raw::IntoRawMode;
|
use raw::IntoRawMode;
|
||||||
|
|
||||||
/// An iterator over input keys.
|
/// An iterator over input keys.
|
||||||
|
@ -41,7 +41,7 @@ impl<R: Read> Iterator for Events<R> {
|
||||||
if let Some(c) = self.leftover {
|
if let Some(c) = self.leftover {
|
||||||
// we have a leftover byte, use it
|
// we have a leftover byte, use it
|
||||||
self.leftover = None;
|
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,
|
// Here we read two bytes at a time. We need to distinguish between single ESC key presses,
|
||||||
|
@ -51,15 +51,17 @@ impl<R: Read> Iterator for Events<R> {
|
||||||
let mut buf = [0u8; 2];
|
let mut buf = [0u8; 2];
|
||||||
let res = match source.read(&mut buf) {
|
let res = match source.read(&mut buf) {
|
||||||
Ok(0) => return None,
|
Ok(0) => return None,
|
||||||
Ok(1) => match buf[0] {
|
Ok(1) => {
|
||||||
b'\x1B' => Ok(Event::Key(Key::Esc)),
|
match buf[0] {
|
||||||
c => parse_event(Ok(c), &mut source.bytes()),
|
b'\x1B' => Ok(Event::Key(Key::Esc)),
|
||||||
},
|
c => parse_event(c, &mut source.bytes()),
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(2) => {
|
Ok(2) => {
|
||||||
let mut option_iter = &mut Some(buf[1]).into_iter();
|
let mut option_iter = &mut Some(buf[1]).into_iter();
|
||||||
let result = {
|
let result = {
|
||||||
let mut iter = option_iter.map(|c| Ok(c)).chain(source.bytes());
|
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.
|
// If the option_iter wasn't consumed, keep the byte for later.
|
||||||
self.leftover = option_iter.next();
|
self.leftover = option_iter.next();
|
||||||
|
@ -69,10 +71,24 @@ impl<R: Read> Iterator for Events<R> {
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(res.or(Ok(Event::Unsupported)))
|
Some(res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_event<I>(item: u8, iter: &mut I) -> Result<Event, io::Error>
|
||||||
|
where I: Iterator<Item = Result<u8, io::Error>>
|
||||||
|
{
|
||||||
|
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.
|
/// Extension to `Read` trait.
|
||||||
pub trait TermRead {
|
pub trait TermRead {
|
||||||
/// An iterator over input events.
|
/// An iterator over input events.
|
||||||
|
@ -105,9 +121,7 @@ impl<R: Read> TermRead for R {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn keys(self) -> Keys<Self> {
|
fn keys(self) -> Keys<Self> {
|
||||||
Keys {
|
Keys { iter: self.events() }
|
||||||
iter: self.events(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_line(&mut self) -> io::Result<Option<String>> {
|
fn read_line(&mut self) -> io::Result<Option<String>> {
|
||||||
|
@ -117,13 +131,16 @@ impl<R: Read> TermRead for R {
|
||||||
match c {
|
match c {
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
Ok(0) | Ok(3) | Ok(4) => return Ok(None),
|
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(b'\n') | Ok(b'\r') => break,
|
||||||
Ok(c) => buf.push(c),
|
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))
|
Ok(Some(string))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,18 +217,25 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_events() {
|
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();
|
\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('b')));
|
||||||
assert_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Char('c')));
|
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::Backspace));
|
||||||
assert_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Left));
|
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(),
|
||||||
assert_eq!(i.next().unwrap().unwrap(), Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4)));
|
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(),
|
||||||
assert_eq!(i.next().unwrap().unwrap(), Event::Mouse(MouseEvent::Release(2, 4)));
|
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::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_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Char('b')));
|
||||||
assert!(i.next().is_none());
|
assert!(i.next().is_none());
|
||||||
}
|
}
|
||||||
|
@ -219,13 +243,14 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_function_keys() {
|
fn test_function_keys() {
|
||||||
let mut st = b"\x1BOP\x1BOQ\x1BOR\x1BOS".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));
|
assert_eq!(st.next().unwrap().unwrap(), Key::F(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut st = b"\x1B[11~\x1B[12~\x1B[13~\x1B[14~\x1B[15~\
|
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();
|
\x1B[17~\x1B[18~\x1B[19~\x1B[20~\x1B[21~\x1B[23~\x1B[24~"
|
||||||
for i in 1 .. 13 {
|
.keys();
|
||||||
|
for i in 1..13 {
|
||||||
assert_eq!(st.next().unwrap().unwrap(), Key::F(i));
|
assert_eq!(st.next().unwrap().unwrap(), Key::F(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -279,14 +304,18 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_backspace() {
|
fn test_backspace() {
|
||||||
line_match("this is the\x7f first\x7f\x7f test", Some("this is th fir test"));
|
line_match("this is the\x7f first\x7f\x7f test",
|
||||||
line_match("this is the seco\x7fnd test\x7f", Some("this is the secnd tes"));
|
Some("this is th fir test"));
|
||||||
|
line_match("this is the seco\x7fnd test\x7f",
|
||||||
|
Some("this is the secnd tes"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_end() {
|
fn test_end() {
|
||||||
line_match("abc\nhttps://www.youtube.com/watch?v=dQw4w9WgXcQ", Some("abc"));
|
line_match("abc\nhttps://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||||
line_match("hello\rhttps://www.youtube.com/watch?v=yPYZpwSpKmA", Some("hello"));
|
Some("abc"));
|
||||||
|
line_match("hello\rhttps://www.youtube.com/watch?v=yPYZpwSpKmA",
|
||||||
|
Some("hello"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Reference in New Issue