diff --git a/src/input/decode.rs b/src/input/decode.rs index dc4163256..84bcc8898 100644 --- a/src/input/decode.rs +++ b/src/input/decode.rs @@ -2,7 +2,7 @@ binding::match_key_event_to_key, input::{ CharEvent, ImplicitEvent, InputEventQueuer, InputEventTrigger, KeyEvent, QueryResponse, - QueryResultEvent, next_input_event, stop_query, + QueryResultEvent, is_event_blocked_when_querying, next_input_event, stop_query, }, }; use crate::{ @@ -36,10 +36,10 @@ trait InputEventQueuerExt: InputEventQueuer { fn on_byte_read(&mut self, read_byte: u8) -> Option { let mut have_escape_prefix = false; let mut buffer = vec![read_byte]; - let mut key = if read_byte == b'\x1b' { + let evt = if read_byte == b'\x1b' { self.parse_escape_sequence(&mut buffer, &mut have_escape_prefix) } else { - canonicalize_control_char(read_byte).map(KeyEvent::from) + None }; if self.paste_is_buffering() { if read_byte != b'\x1b' { @@ -47,71 +47,51 @@ fn on_byte_read(&mut self, read_byte: u8) -> Option { } return None; } - let mut seq = WString::new(); - if key.is_some_and(|key| key.key == Key::from_raw(key::INVALID)) { + if evt.as_ref().is_some_and(|key_evt| { + key_evt + .get_key() + .is_some_and(|key_evt| key_evt.key.codepoint == key::INVALID) + }) { return None; } - assert!(key.is_none_or(|key| key.codepoint != key::INVALID)); - // At this point, the bytes in `buffer` should be parsed as a UTF-8 sequence, - // or, if they are not valid UTF-8, ignored. On incomplete sequences, another - // byte is read and decoding is tried again in the next iteration. - let ok = loop { - match decode_utf8(&mut seq, InvalidPolicy::Error, &buffer) { - DecodeState::Incomplete => { - buffer.push( - match next_input_event( - self.get_in_fd(), - self.get_ioport_fd(), - Timeout::Forever, - ) { - InputEventTrigger::Byte(b) => b, - _ => 0, - }, - ); - } - DecodeState::Complete => { - if have_escape_prefix { - let c = seq.as_char_slice().last().unwrap(); - key = Some(KeyEvent::from(alt(*c))); - } - break true; - } - DecodeState::Error => { - self.push_front(CharEvent::from_check_exit()); - break false; - } + let decoded_buffer = match decode_utf8_at_least_one(&mut buffer, |buffer| { + buffer.push( + match next_input_event(self.get_in_fd(), self.get_ioport_fd(), Timeout::Forever) { + InputEventTrigger::Byte(b) => b, + _ => 0, + }, + ); + }) { + Ok(decoded) => decoded, + Err(evt) => { + return Some(evt); } }; - if !ok { - return None; - } - let (key_evt, extra) = if let Some(key) = key { - (CharEvent::from_key_seq(key, seq), None) - } else { - let c = seq.chars().next()?; - ( - CharEvent::from_key_seq(KeyEvent::from_raw(c), seq.clone()), - Some(seq.chars().skip(1).map(CharEvent::from_char)), - ) - }; - if self.is_blocked_querying() { - flog!( - reader, - "Still blocked on response from terminal, deferring key event", - key_evt - ); - self.push_back(key_evt); - extra.map(|extra| { - for evt in extra { - self.push_back(evt); - } + let mut evt = evt + .or_else(|| { + let code_unit_first_byte = *buffer.last().unwrap(); + canonicalize_control_char(code_unit_first_byte) + .map(KeyEvent::from) + .map(|key_evt| CharEvent::from_key_seq(key_evt, decoded_buffer.clone())) + }) + .unwrap_or_else(|| { + let codepoint = decoded_buffer.chars().next_back().unwrap(); + let key_evt = KeyEvent::from_raw(codepoint); + CharEvent::from_key_seq(key_evt, decoded_buffer) }); - let vintr = shell_modes().control_chars[libc::VINTR]; - if vintr != 0 - && key.is_some_and(|key| { - match_key_event_to_key(&key, &Key::from_single_byte(vintr)).is_some() - }) - { + if have_escape_prefix { + if let Some(key) = evt.get_key_mut() { + key.key.modifiers.alt = true; + } + } + if self.is_blocked_querying() && is_event_blocked_when_querying(&evt) { + if { + let vintr = shell_modes().control_chars[libc::VINTR]; + vintr != 0 + && evt.get_key().is_some_and(|key| { + match_key_event_to_key(&key.key, &Key::from_single_byte(vintr)).is_some() + }) + } { flog!( reader, "Received interrupt key, giving up waiting for response from terminal" @@ -120,37 +100,46 @@ fn on_byte_read(&mut self, read_byte: u8) -> Option { assert!(ok); self.get_input_data_mut().queue.clear(); self.push_front(CharEvent::QueryResult(QueryResultEvent::Interrupted)); + return None; } + flog!( + reader, + "Still blocked on response from terminal, deferring key event", + evt + ); + self.push_back(evt); return None; } - extra.map(|extra| self.insert_front(extra)); - Some(key_evt) + Some(evt) } fn parse_escape_sequence( &mut self, buffer: &mut Vec, have_escape_prefix: &mut bool, - ) -> Option { + ) -> Option { assert!(matches!(buffer.as_slice(), b"\x1b" | b"\x1b\x1b")); let recursive_invocation = buffer.len() == 2; let Some(next) = self.read_sequence_byte(buffer) else { - return Some(KeyEvent::from_raw(key::ESCAPE)); + return char_event(buffer, KeyEvent::from_raw(key::ESCAPE)); }; - let invalid = KeyEvent::from_raw(key::INVALID); + let invalid = CharEvent::from_key(KeyEvent::from_raw(key::INVALID)); if !recursive_invocation && next == b'\x1b' { - return Some( - match self.parse_escape_sequence(buffer, have_escape_prefix) { - Some(mut nested_sequence) => { - if nested_sequence.key == invalid.key { - return Some(KeyEvent::from_raw(key::ESCAPE)); - } - nested_sequence.modifiers.alt = true; - nested_sequence + return match self.parse_escape_sequence(buffer, have_escape_prefix) { + Some(mut nested_sequence) => { + if nested_sequence + .get_key() + .is_some_and(|key_evt| key_evt.key.codepoint == key::INVALID) + { + return char_event(buffer, KeyEvent::from_raw(key::ESCAPE)); } - _ => invalid, - }, - ); + if let Some(key_evt) = nested_sequence.get_key_mut() { + key_evt.key.modifiers.alt = true; + } + Some(nested_sequence) + } + _ => Some(invalid), + }; } if next == b'[' { // potential CSI @@ -171,30 +160,22 @@ fn parse_escape_sequence( return Some(self.parse_dcs(buffer).unwrap_or(invalid)); } } - match canonicalize_control_char(next) { - Some(mut key) => { - key.modifiers.alt = true; - Some(KeyEvent::from(key)) - } - None => { - *have_escape_prefix = true; - None - } - } + *have_escape_prefix = true; + None } - fn parse_csi(&mut self, buffer: &mut Vec) -> Option { + fn parse_csi(&mut self, buffer: &mut Vec) -> Option { // The maximum number of CSI parameters is defined by NPAR, nominally 16. let mut params = [[0_u32; 4]; 16]; let Some(mut c) = self.read_sequence_byte(buffer) else { - return Some(KeyEvent::from(alt('['))); + return char_event(buffer, KeyEvent::from(alt('['))); }; let mut next_char = |zelf: &mut Self| zelf.read_sequence_byte(buffer).unwrap_or(0xff); let private_mode; match c { b'[' => { // Illegal CSI command. - return Some(KeyEvent::new( + let key_evt = KeyEvent::new( Modifiers::default(), function_key(match next_char(self) { b'A' => 1, @@ -204,7 +185,8 @@ fn parse_csi(&mut self, buffer: &mut Vec) -> Option { b'E' => 5, _ => return invalid_sequence(buffer), }), - )); + ); + return char_event(buffer, key_evt); } b'?' | b'<' | b'=' | b'>' => { private_mode = Some(c); @@ -327,8 +309,7 @@ fn parse_csi(&mut self, buffer: &mut Vec) -> Option { if code != 0 || c != b'M' || modifiers.is_some() { return None; } - self.push_front(CharEvent::Implicit(ImplicitEvent::MouseLeft(position))); - return None; + return Some(CharEvent::Implicit(ImplicitEvent::MouseLeft(position))); } b't' => { flog!(reader, "mouse event"); @@ -362,8 +343,7 @@ fn parse_csi(&mut self, buffer: &mut Vec) -> Option { }; flog!(reader, "Received cursor position report y:", y, "x:", x); let cursor_pos = ViewportPosition { x, y }; - self.push_query_response(QueryResponse::CursorPosition(cursor_pos)); - return None; + return Some(query_response(QueryResponse::CursorPosition(cursor_pos))); } b'S' => masked_key(function_key(4)), b'~' => match params[0][0] { @@ -414,8 +394,7 @@ fn parse_csi(&mut self, buffer: &mut Vec) -> Option { }, b'c' if private_mode == Some(b'?') => { flog!(reader, "Received Primary Device Attribute response"); - self.push_query_response(QueryResponse::PrimaryDeviceAttribute); - return None; + return Some(query_response(QueryResponse::PrimaryDeviceAttribute)); } b'n' if private_mode == Some(b'?') && params[0] == [997, 0, 0, 0] => { match params[1] { @@ -423,8 +402,7 @@ fn parse_csi(&mut self, buffer: &mut Vec) -> Option { _ => return None, } flog!(reader, "Received color theme change"); - self.push_front(CharEvent::Implicit(ImplicitEvent::NewColorTheme)); - return None; + return Some(CharEvent::Implicit(ImplicitEvent::NewColorTheme)); } b'u' => { if private_mode == Some(b'?') { @@ -484,22 +462,20 @@ fn parse_csi(&mut self, buffer: &mut Vec) -> Option { } b'Z' => KeyEvent::from(shift(key::TAB)), b'I' => { - self.push_front(CharEvent::Implicit(ImplicitEvent::FocusIn)); - return None; + return Some(CharEvent::Implicit(ImplicitEvent::FocusIn)); } b'O' => { - self.push_front(CharEvent::Implicit(ImplicitEvent::FocusOut)); - return None; + return Some(CharEvent::Implicit(ImplicitEvent::FocusOut)); } _ => return None, }; - Some(key) + char_event(buffer, key) } - fn parse_ss3(&mut self, buffer: &mut Vec) -> Option { + fn parse_ss3(&mut self, buffer: &mut Vec) -> Option { let mut raw_mask = 0; let Some(mut code) = self.read_sequence_byte(buffer) else { - return Some(KeyEvent::from(alt('O'))); + return char_event(buffer, KeyEvent::from(alt('O'))); }; while code.is_ascii_digit() { raw_mask = raw_mask * 10 + u32::from(code - b'0'); @@ -540,7 +516,7 @@ fn parse_ss3(&mut self, buffer: &mut Vec) -> Option { b'y' => KeyEvent::new(modifiers, '9'), _ => return None, }; - Some(key) + char_event(buffer, key) } fn read_until_sequence_terminator( @@ -581,7 +557,7 @@ fn parse_xtversion(&mut self, buffer: &mut Vec) -> Option<()> { None } - fn parse_osc(&mut self, buffer: &mut Vec) -> Option<()> { + fn parse_osc(&mut self, buffer: &mut Vec) -> Option { let osc_prefix = b"\x1b]"; assert_eq!(buffer, osc_prefix); self.read_until_sequence_terminator(buffer, /*allow_bel=*/ true)?; @@ -589,14 +565,13 @@ fn parse_osc(&mut self, buffer: &mut Vec) -> Option<()> { let buffer = buffer.strip_prefix(b"11;")?; let c = xterm_color::Color::parse(buffer).ok()?; flog!(reader, format!("Received background color {c:?}")); - self.push_query_response(QueryResponse::BackgroundColor(c)); - None + Some(query_response(QueryResponse::BackgroundColor(c))) } - fn parse_dcs(&mut self, buffer: &mut Vec) -> Option { + fn parse_dcs(&mut self, buffer: &mut Vec) -> Option { assert_eq!(buffer, b"\x1bP"); let Some(success) = self.read_sequence_byte(buffer) else { - return Some(KeyEvent::from(alt('P'))); + return char_event(buffer, KeyEvent::from(alt('P'))); }; let success = match success { b'0' => false, @@ -659,6 +634,39 @@ fn parse_dcs(&mut self, buffer: &mut Vec) -> Option { } } +fn decode_utf8_at_least_one( + buffer: &mut Vec, + on_incomplete: impl Fn(&mut Vec), +) -> Result { + let mut decoded_input = WString::new(); + loop { + match decode_utf8(&mut decoded_input, InvalidPolicy::Error, buffer) { + DecodeState::Incomplete => { + on_incomplete(buffer); + } + DecodeState::Complete => { + return Ok(decoded_input); + } + DecodeState::Error => { + return Err(CharEvent::from_check_exit()); + } + } + } +} + +fn char_event(buffer: &mut Vec, key_evt: KeyEvent) -> Option { + Some({ + match decode_utf8_at_least_one(buffer, |_buffer| panic!()) { + Ok(decoded_buffer) => CharEvent::from_key_seq(key_evt, decoded_buffer), + Err(evt) => evt, + } + }) +} + +fn query_response(response: QueryResponse) -> CharEvent { + CharEvent::QueryResult(QueryResultEvent::Response(response)) +} + fn parse_mask(mask: u32) -> (Modifiers, bool) { let modifiers = Modifiers { ctrl: (mask & 4) != 0, @@ -728,7 +736,7 @@ pub(crate) fn decode_utf8( } } -fn invalid_sequence(buffer: &[u8]) -> Option { +fn invalid_sequence(buffer: &[u8]) -> Option { flog!( reader, "Error: invalid escape sequence: ", diff --git a/src/input/input.rs b/src/input/input.rs index 757f8fcbe..c7ba90e55 100644 --- a/src/input/input.rs +++ b/src/input/input.rs @@ -123,6 +123,13 @@ pub fn get_key(&self) -> Option<&KeyInputEvent> { } } + pub fn get_key_mut(&mut self) -> Option<&mut KeyInputEvent> { + match self { + CharEvent::Key(kevt) => Some(kevt), + _ => None, + } + } + pub fn get_readline(&self) -> ReadlineCmd { let CharEvent::Readline(c) = self else { panic!("Not a readline type"); @@ -404,22 +411,28 @@ pub enum TerminalQuery { Recurrent(RecurrentQuery), } +pub(super) fn is_event_blocked_when_querying(evt: &CharEvent) -> bool { + use ImplicitEvent::*; + match evt { + CharEvent::QueryResult(_) | CharEvent::Implicit(CheckExit | Eof) => false, + CharEvent::Key(_) + | CharEvent::Readline(_) + | CharEvent::Command(_) + | CharEvent::Implicit(_) => { + true // No code execution while blocked. + } + } +} + /// A trait which knows how to produce a stream of input events. /// Note this is conceptually a "base class" with override points. pub trait InputEventQueuer { /// Return the next event in the queue, or none if the queue is empty. fn try_pop(&mut self) -> Option { - if self.is_blocked_querying() { - use ImplicitEvent::*; - match self.get_input_data().queue.front()? { - CharEvent::QueryResult(_) | CharEvent::Implicit(CheckExit | Eof) => {} - CharEvent::Key(_) - | CharEvent::Readline(_) - | CharEvent::Command(_) - | CharEvent::Implicit(_) => { - return None; // No code execution while blocked. - } - } + if self.is_blocked_querying() + && is_event_blocked_when_querying(self.get_input_data().queue.front()?) + { + return None; } self.get_input_data_mut().queue.pop_front() } @@ -564,10 +577,6 @@ fn push_front(&mut self, ch: CharEvent) { self.get_input_data_mut().queue.push_front(ch); } - fn push_query_response(&mut self, response: QueryResponse) { - self.push_front(CharEvent::QueryResult(QueryResultEvent::Response(response))); - } - /// Find the first sequence of non-char events, and promote them to the front. fn promote_interruptions_to_front(&mut self) { // Find the first sequence of non-char events.