From d57468cdc106699585dd0fea0361998293c26574 Mon Sep 17 00:00:00 2001 From: Sebastian Egbers Date: Fri, 16 Oct 2020 11:26:49 +0200 Subject: [PATCH] Added Ctrl/Alt/Shift+Navigation key support. --- src/event.rs | 414 +++++++++++++++++++++++++++++---------------------- 1 file changed, 237 insertions(+), 177 deletions(-) diff --git a/src/event.rs b/src/event.rs index 8e36d5d..0d84353 100644 --- a/src/event.rs +++ b/src/event.rs @@ -57,16 +57,44 @@ pub enum Key { Backspace, /// Left arrow. Left, + /// Shift Left arrow. + ShiftLeft, + /// Alt Left arrow. + AltLeft, + /// Ctrl Left arrow. + CtrlLeft, /// Right arrow. Right, + /// Shift Right arrow. + ShiftRight, + /// Alt Right arrow. + AltRight, + /// Ctrl Right arrow. + CtrlRight, /// Up arrow. Up, + /// Shift Up arrow. + ShiftUp, + /// Alt Up arrow. + AltUp, + /// Ctrl Up arrow. + CtrlUp, /// Down arrow. Down, + /// Shift Down arrow. + ShiftDown, + /// Alt Down arrow. + AltDown, + /// Ctrl Down arrow + CtrlDown, /// Home key. Home, + /// Ctrl Home key. + CtrlHome, /// End key. End, + /// Ctrl End key. + CtrlEnd, /// Page Up key. PageUp, /// Page Down key. @@ -100,30 +128,31 @@ pub enum Key { /// Parse an Event from `item` and possibly subsequent bytes through `iter`. pub fn parse_event(item: u8, iter: &mut I) -> Result - where I: Iterator> + where + I: Iterator>, { let error = Error::new(ErrorKind::Other, "Could not parse an event"); match item { b'\x1B' => { // This is an escape character, leading a control sequence. 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 Err(error), + 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 Err(error), + } } - } - Some(Ok(b'[')) => { - // This is a CSI sequence. - parse_csi(iter).ok_or(error)? - } - Some(Ok(c)) => { - let ch = parse_utf8_char(c, iter)?; - Event::Key(Key::Alt(ch)) - } - Some(Err(_)) | None => return Err(error), - }) + Some(Ok(b'[')) => { + // This is a CSI sequence. + parse_csi(iter).ok_or(error)? + } + Some(Ok(c)) => { + let ch = parse_utf8_char(c, iter)?; + Event::Key(Key::Alt(ch)) + } + Some(Err(_)) | None => return Err(error), + }) } b'\n' | b'\r' => Ok(Event::Key(Key::Char('\n'))), b'\t' => Ok(Event::Key(Key::Char('\t'))), @@ -131,12 +160,10 @@ pub fn parse_event(item: u8, iter: &mut I) -> Result c @ b'\x01'..=b'\x1A' => Ok(Event::Key(Key::Ctrl((c as u8 - 0x1 + b'a') as char))), c @ b'\x1C'..=b'\x1F' => Ok(Event::Key(Key::Ctrl((c as u8 - 0x1C + b'4') as char))), b'\0' => Ok(Event::Key(Key::Null)), - c => { - Ok({ - let ch = parse_utf8_char(c, iter)?; - Event::Key(Key::Char(ch)) - }) - } + c => Ok({ + let ch = parse_utf8_char(c, iter)?; + Event::Key(Key::Char(ch)) + }), } } @@ -144,179 +171,212 @@ pub fn parse_event(item: u8, iter: &mut I) -> Result /// /// Returns None if an unrecognized sequence is found. fn parse_csi(iter: &mut I) -> Option - where I: Iterator> + where + I: Iterator>, { Some(match iter.next() { - Some(Ok(b'[')) => match iter.next() { - Some(Ok(val @ b'A'..=b'E')) => Event::Key(Key::F(1 + val - b'A')), - _ => return None, - }, - 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'Z')) => Event::Key(Key::BackTab), - 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::() - .unwrap(); - let cx = nums.next() - .unwrap() - .parse::() - .unwrap(); - let cy = nums.next() - .unwrap() - .parse::() - .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), - 3 => MouseEvent::Release(cx, cy), + Some(Ok(b'[')) => match iter.next() { + Some(Ok(val @ b'A'..=b'E')) => Event::Key(Key::F(1 + val - b'A')), _ => return None, - }; + }, + Some(Ok(b'1')) => { + iter.next(); // Consume next char (;) + match iter.next() { + Some(Ok(b'2')) => { // Shift Modifier + match iter.next() { + Some(Ok(b'D')) => Event::Key(Key::ShiftLeft), + Some(Ok(b'C')) => Event::Key(Key::ShiftRight), + Some(Ok(b'A')) => Event::Key(Key::ShiftUp), + Some(Ok(b'B')) => Event::Key(Key::ShiftDown), + _ => return None, + } + } + Some(Ok(b'3')) => { // Alt Modifier + match iter.next() { + Some(Ok(b'D')) => Event::Key(Key::AltLeft), + Some(Ok(b'C')) => Event::Key(Key::AltRight), + Some(Ok(b'A')) => Event::Key(Key::AltUp), + Some(Ok(b'B')) => Event::Key(Key::AltDown), + _ => return None, + } + } + Some(Ok(b'5')) => { // Ctrl Modifier + match iter.next() { + Some(Ok(b'D')) => Event::Key(Key::CtrlLeft), + Some(Ok(b'C')) => Event::Key(Key::CtrlRight), + Some(Ok(b'A')) => Event::Key(Key::CtrlUp), + Some(Ok(b'B')) => Event::Key(Key::CtrlDown), + Some(Ok(b'H')) => Event::Key(Key::CtrlHome), + Some(Ok(b'F')) => Event::Key(Key::CtrlEnd), + _ => return None, + } + } + _ => return None, + } + } + 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'Z')) => Event::Key(Key::BackTab), + Some(Ok(b'M')) => { + // X10 emulation mouse encoding: ESC [ CB Cx Cy (6 characters only). + let mut next = || iter.next().unwrap().unwrap(); - 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 { + 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::().unwrap(); + let cx = nums.next().unwrap().parse::().unwrap(); + let cy = nums.next().unwrap().parse::().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), + 3 => MouseEvent::Release(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); - 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 = 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) + 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(); } - // 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 = str_buf.split(';').map(|n| n.parse().unwrap()).collect(); + match c { + // rxvt mouse encoding: + // ESC [ Cb ; Cx ; Cy ; M + b'M' => { + let str_buf = String::from_utf8(buf).unwrap(); - if nums.is_empty() { - return None; + let nums: Vec = 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(); - // TODO: handle multiple values for key modififiers (ex: values - // [3, 2] means Shift+Delete) - if nums.len() > 1 { - return None; - } + // This CSI sequence can be a list of semicolon-separated + // numbers. + let nums: Vec = str_buf.split(';').map(|n| n.parse().unwrap()).collect(); - 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, + 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)), + v => { + println!("{:?}", v); + return None; + } + } } + _ => return None, } - _ => return None, } - } - _ => return None, - }) - + _ => return None, + }) } /// Parse `c` as either a single byte ASCII char or a variable size UTF-8 char. fn parse_utf8_char(c: u8, iter: &mut I) -> Result - where I: Iterator> + where + I: Iterator>, { - 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() { Ok(c as char) } else {