From 0e74a7672fd8528b1f62e2c6be151000a5948eec Mon Sep 17 00:00:00 2001 From: IGI-111 Date: Fri, 15 Jul 2016 07:41:31 +0200 Subject: [PATCH 1/6] added more special keys Key now supports Home, End, PageUp, PageDown, Delete, Insert and Function keys. All this is done through the detection of both VT100 escape codes and more modern standard counterparts. For instance, F2 can be both ESC OQ on VT100, screen, and some versions of xterm and ESC [12~ on rxvt and other xterm versions depending on your terminal --- src/input.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/input.rs b/src/input.rs index 7258d34..921ccb1 100644 --- a/src/input.rs +++ b/src/input.rs @@ -15,6 +15,20 @@ pub enum Key { Up, /// Down arrow. Down, + /// Home key. + Home, + /// End key. + End, + /// Page Up key. + PageUp, + /// Page Down key. + PageDown, + /// Delete key. + Delete, + /// Insert key. + Insert, + /// Function keys. + F(u8), /// Normal character. Char(char), /// Alt modified character. @@ -28,6 +42,7 @@ pub enum Key { /// Null byte. Null, + #[allow(missing_docs)] #[doc(hidden)] __IsNotComplete @@ -46,11 +61,50 @@ impl>> Iterator for Keys { fn next(&mut self) -> Option> { Some(match self.chars.next() { Some(Ok('\x1B')) => Ok(match self.chars.next() { + Some(Ok('O')) => match self.chars.next() { + Some(Ok('P')) => Key::F(1), + Some(Ok('Q')) => Key::F(2), + Some(Ok('R')) => Key::F(3), + Some(Ok('S')) => Key::F(4), + _ => Key::Invalid, + }, Some(Ok('[')) => match self.chars.next() { Some(Ok('D')) => Key::Left, Some(Ok('C')) => Key::Right, Some(Ok('A')) => Key::Up, Some(Ok('B')) => Key::Down, + Some(Ok('H')) => Key::Home, + Some(Ok('F')) => Key::End, + Some(Ok(c @ '1' ... '6')) => match self.chars.next() { + Some(Ok('~')) => match c { + '1' => Key::Home, + '2' => Key::Insert, + '3' => Key::Delete, + '4' => Key::End, + '5' => Key::PageUp, + '6' => Key::PageDown, + _ => Key::Invalid, + }, + Some(Ok(k @ '0' ... '9')) => match self.chars.next() { + Some(Ok('~')) => match (c, k) { + ('1', '1') => Key::F(1), + ('1', '2') => Key::F(2), + ('1', '3') => Key::F(3), + ('1', '4') => Key::F(4), + ('1', '5') => Key::F(5), + ('1', '7') => Key::F(6), + ('1', '8') => Key::F(7), + ('1', '9') => Key::F(8), + ('2', '0') => Key::F(9), + ('2', '1') => Key::F(10), + ('2', '3') => Key::F(11), + ('2', '4') => Key::F(12), + _ => Key::Invalid, + }, + _ => Key::Invalid, + }, + _ => Key::Invalid, + }, _ => Key::Invalid, }, Some(Ok(c)) => Key::Alt(c), From 3552c6eae0e6abf97a969b5785fcf2deacd239b1 Mon Sep 17 00:00:00 2001 From: IGI-111 Date: Fri, 15 Jul 2016 12:19:02 +0200 Subject: [PATCH 2/6] added rxvt Home and End escape codes --- src/input.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/input.rs b/src/input.rs index 921ccb1..46ff6f3 100644 --- a/src/input.rs +++ b/src/input.rs @@ -77,8 +77,8 @@ impl>> Iterator for Keys { Some(Ok('F')) => Key::End, Some(Ok(c @ '1' ... '6')) => match self.chars.next() { Some(Ok('~')) => match c { - '1' => Key::Home, - '2' => Key::Insert, + '1' | '7' => Key::Home, + '2' | '8' => Key::Insert, '3' => Key::Delete, '4' => Key::End, '5' => Key::PageUp, From 70c12b20d6a772084450ae3b1ed2b6435670f9ca Mon Sep 17 00:00:00 2001 From: IGI-111 Date: Sat, 16 Jul 2016 18:48:00 +0200 Subject: [PATCH 3/6] added doc for function keys --- src/input.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/input.rs b/src/input.rs index 46ff6f3..d89893f 100644 --- a/src/input.rs +++ b/src/input.rs @@ -28,6 +28,8 @@ pub enum Key { /// Insert key. Insert, /// Function keys. + /// + /// Only function keys 1 through 12 are supported. F(u8), /// Normal character. Char(char), From 5eae7cf7324c080611af4e6fd3bc3a03b3897f58 Mon Sep 17 00:00:00 2001 From: IGI-111 Date: Sat, 16 Jul 2016 19:10:04 +0200 Subject: [PATCH 4/6] function keys now use ranges for detection --- src/input.rs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/input.rs b/src/input.rs index d89893f..21a80dd 100644 --- a/src/input.rs +++ b/src/input.rs @@ -88,19 +88,10 @@ impl>> Iterator for Keys { _ => Key::Invalid, }, Some(Ok(k @ '0' ... '9')) => match self.chars.next() { - Some(Ok('~')) => match (c, k) { - ('1', '1') => Key::F(1), - ('1', '2') => Key::F(2), - ('1', '3') => Key::F(3), - ('1', '4') => Key::F(4), - ('1', '5') => Key::F(5), - ('1', '7') => Key::F(6), - ('1', '8') => Key::F(7), - ('1', '9') => Key::F(8), - ('2', '0') => Key::F(9), - ('2', '1') => Key::F(10), - ('2', '3') => Key::F(11), - ('2', '4') => Key::F(12), + Some(Ok('~')) => match 10 * (c as u8 - b'0') + (k as u8 - b'0') { + v @ 11 ... 15 => Key::F(v - 10), + v @ 17 ... 21 => Key::F(v - 11), + v @ 23 ... 24 => Key::F(v - 12), _ => Key::Invalid, }, _ => Key::Invalid, From 4402ebd8b38baed0fc3e4f0e8eda8f6c800b40ed Mon Sep 17 00:00:00 2001 From: IGI-111 Date: Sat, 16 Jul 2016 19:21:51 +0200 Subject: [PATCH 5/6] fix wrong range used --- src/input.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input.rs b/src/input.rs index 21a80dd..fdb8da2 100644 --- a/src/input.rs +++ b/src/input.rs @@ -77,7 +77,7 @@ impl>> Iterator for Keys { Some(Ok('B')) => Key::Down, Some(Ok('H')) => Key::Home, Some(Ok('F')) => Key::End, - Some(Ok(c @ '1' ... '6')) => match self.chars.next() { + Some(Ok(c @ '1' ... '8')) => match self.chars.next() { Some(Ok('~')) => match c { '1' | '7' => Key::Home, '2' | '8' => Key::Insert, From 2f6ebb8669c72941bb21cf88a7a4d56de01d54e4 Mon Sep 17 00:00:00 2001 From: IGI-111 Date: Sat, 16 Jul 2016 22:46:29 +0200 Subject: [PATCH 6/6] added tests --- src/input.rs | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/input.rs b/src/input.rs index fdb8da2..824bb49 100644 --- a/src/input.rs +++ b/src/input.rs @@ -80,9 +80,9 @@ impl>> Iterator for Keys { Some(Ok(c @ '1' ... '8')) => match self.chars.next() { Some(Ok('~')) => match c { '1' | '7' => Key::Home, - '2' | '8' => Key::Insert, + '2'=> Key::Insert, '3' => Key::Delete, - '4' => Key::End, + '4' | '8' => Key::End, '5' => Key::PageUp, '6' => Key::PageDown, _ => Key::Invalid, @@ -183,6 +183,36 @@ mod test { assert!(i.next().is_none()); } + #[cfg(feature = "nightly")] + #[test] + fn test_function_keys() { + let mut st = b"\x1BOP\x1BOQ\x1BOR\x1BOS".keys(); + for i in 1 .. 5 { + assert_eq!(st.next().unwrap().unwrap(), Key::F(i)); + } + + 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(); + for i in 1 .. 13 { + assert_eq!(st.next().unwrap().unwrap(), Key::F(i)); + } + } + + #[cfg(feature = "nightly")] + #[test] + fn test_special_keys() { + let mut st = b"\x1B[2~\x1B[H\x1B[7~\x1B[5~\x1B[3~\x1B[F\x1B[8~\x1B[6~".keys(); + assert_eq!(st.next().unwrap().unwrap(), Key::Insert); + assert_eq!(st.next().unwrap().unwrap(), Key::Home); + assert_eq!(st.next().unwrap().unwrap(), Key::Home); + assert_eq!(st.next().unwrap().unwrap(), Key::PageUp); + assert_eq!(st.next().unwrap().unwrap(), Key::Delete); + assert_eq!(st.next().unwrap().unwrap(), Key::End); + assert_eq!(st.next().unwrap().unwrap(), Key::End); + assert_eq!(st.next().unwrap().unwrap(), Key::PageDown); + assert!(st.next().is_none()); + } + fn line_match(a: &str, b: Option<&str>) { let mut sink = io::sink(); @@ -226,4 +256,5 @@ mod test { line_match("abc\x03https://www.youtube.com/watch?v=dQw4w9WgXcQ", None); line_match("hello\x04https://www.youtube.com/watch?v=yPYZpwSpKmA", None); } + }