diff options
| author | kangal-bot <kangal@herdr.dev> | 2026-05-26 19:59:15 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-05-26 19:59:15 +0300 |
| commit | 31e973502af205666cb4a48e3f9e10328f65cd4e (patch) | |
| tree | 67ddda76c3d6ac551e9ae17ecaa4fce675f94262 /src/raw_input.rs | |
| parent | 5210b08e7db000d619b365fbcb8ec08104b0fcaa (diff) | |
fix: buffer split host color replies (#310)
refs #306
Co-authored-by: kangal-bot <kangal-bot@users.noreply.github.com>
Diffstat (limited to 'src/raw_input.rs')
| -rw-r--r-- | src/raw_input.rs | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/src/raw_input.rs b/src/raw_input.rs index fb9a1a3d..6c3878bf 100644 --- a/src/raw_input.rs +++ b/src/raw_input.rs @@ -216,6 +216,14 @@ pub(crate) fn flush_incomplete_input_bytes(buffer: &mut Vec<u8>) -> Option<Vec<u return None; } + if starts_with_incomplete_default_color_response(buffer) { + tracing::trace!( + len = buffer.len(), + "waiting for host color response terminator" + ); + return None; + } + if buffer.as_slice() == [ESC] { tracing::warn!( bytes = ?buffer, @@ -334,6 +342,11 @@ fn extract_one_event(buffer: &[u8]) -> Option<(RawInputEvent, usize)> { Some((RawInputEvent::Key(key), consumed)) } +fn starts_with_incomplete_default_color_response(buffer: &[u8]) -> bool { + find_osc_terminator(buffer).is_none() + && matches!(buffer.get(..5), Some(b"\x1b]10;" | b"\x1b]11;")) +} + fn first_complete_utf8_char_len(buffer: &[u8]) -> Option<usize> { let width = utf8_char_width(*buffer.first()?)?; @@ -1285,4 +1298,84 @@ mod tests { assert_eq!(ranges[0].len, input.len()); assert!(matches!(&ranges[0].event, RawInputEvent::Mouse(_))); } + + #[test] + fn parses_ghostty_default_background_response() { + let events = parse_raw_input_bytes_sync(b"\x1b]11;rgb:2828/2a2a/3636\x07"); + + assert_eq!(events.len(), 1); + assert!(matches!( + events[0], + RawInputEvent::HostDefaultColor { + kind: DefaultColorKind::Background, + color: RgbColor { + r: 0x28, + g: 0x2a, + b: 0x36 + } + } + )); + } + + #[test] + fn drain_complete_input_bytes_keeps_split_default_background_response_buffered() { + let mut buffer = b"\x1b]11;rgb:2828".to_vec(); + + let chunks = drain_complete_input_bytes(&mut buffer); + + assert!(chunks.is_empty()); + assert_eq!(buffer, b"\x1b]11;rgb:2828"); + } + + #[test] + fn flush_incomplete_input_bytes_keeps_split_default_background_response_buffered() { + let mut buffer = b"\x1b]11;rgb:2828".to_vec(); + + let flushed = flush_incomplete_input_bytes(&mut buffer); + + assert!(flushed.is_none()); + assert_eq!(buffer, b"\x1b]11;rgb:2828"); + } + + #[test] + fn flush_incomplete_input_bytes_keeps_default_background_response_split_after_command() { + let mut buffer = b"\x1b]11;".to_vec(); + + let flushed = flush_incomplete_input_bytes(&mut buffer); + + assert!(flushed.is_none()); + assert_eq!(buffer, b"\x1b]11;"); + } + + #[test] + fn flush_incomplete_input_bytes_keeps_default_background_response_split_inside_st() { + let mut buffer = b"\x1b]11;rgb:2828/2a2a/3636\x1b".to_vec(); + + let flushed = flush_incomplete_input_bytes(&mut buffer); + + assert!(flushed.is_none()); + assert_eq!(buffer, b"\x1b]11;rgb:2828/2a2a/3636\x1b"); + } + + #[test] + fn non_osc_default_color_text_remains_key_input() { + let events = parse_raw_input_bytes_sync(b"11;rgb:2828/2a2a/3636\x07"); + + assert_eq!(events.len(), 22); + assert_raw_key( + events.into_iter().next().unwrap(), + KeyCode::Char('1'), + KeyModifiers::empty(), + ); + } + + #[test] + fn flush_incomplete_input_bytes_does_not_hold_non_osc_default_color_text() { + let mut buffer = b"11;rgb:2828".to_vec(); + + let flushed = flush_incomplete_input_bytes(&mut buffer); + + assert_eq!(flushed, None); + assert!(buffer.is_empty()); + } } |
