aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-05-29 19:48:55 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-05-29 19:48:55 +0300
commit77242943ba65a80cbc1180258dd64f06635233f9 (patch)
treeaf094d9f591f7886578e109fc9820d7516a17b1a /src/client
parent324e625c60658ab0de46928be300fc1804af48df (diff)
fix: keep split host color replies out of panes
refs #344 refs #279
Diffstat (limited to 'src/client')
-rw-r--r--src/client/input.rs22
1 files changed, 10 insertions, 12 deletions
diff --git a/src/client/input.rs b/src/client/input.rs
index 31e1c1e4..e9592db8 100644
--- a/src/client/input.rs
+++ b/src/client/input.rs
@@ -1,14 +1,15 @@
//! Stdin input reading for the thin client.
//!
-//! Reads raw bytes from stdin and forwards them to the main event loop.
-//! Unlike the monolithic herdr, the thin client does NOT parse the input
-//! into key/mouse/paste events — it just sends the raw bytes to the server
-//! as `ClientMessage::Input`. The server handles parsing.
+//! Reads stdin bytes and forwards framed input to the main event loop.
+//! Unlike the monolithic herdr, the thin client does NOT parse input into
+//! key/mouse/paste events. It keeps enough byte-framing state to avoid splitting
+//! terminal control strings, then sends bytes to the server as `ClientMessage::Input`.
+//! The server handles semantic parsing.
//!
//! This is simpler and more reliable because:
//! - The server has the same input parsing code
//! - We avoid duplicating parsing logic in the client
-//! - Raw forwarding preserves all escape sequences faithfully
+//! - Host terminal control replies can be buffered or discarded before they leak
use std::io::{self, Read};
use std::sync::atomic::{AtomicBool, Ordering};
@@ -33,15 +34,13 @@ pub fn stdin_reader_loop(event_tx: mpsc::Sender<ClientLoopEvent>, should_quit: &
let stdin = io::stdin();
let mut reader = stdin.lock();
let mut scratch = [0u8; 4096];
- let mut buffer = Vec::new();
+ let mut framer = crate::raw_input::RawInputByteFramer::default();
while !should_quit.load(Ordering::Acquire) {
match reader.read(&mut scratch) {
Ok(0) => break,
Ok(n) => {
- buffer.extend_from_slice(&scratch[..n]);
-
- for data in crate::raw_input::drain_complete_input_bytes(&mut buffer) {
+ for data in framer.push(&scratch[..n]) {
if event_tx
.blocking_send(ClientLoopEvent::StdinInput(data))
.is_err()
@@ -50,9 +49,8 @@ pub fn stdin_reader_loop(event_tx: mpsc::Sender<ClientLoopEvent>, should_quit: &
}
}
- if !buffer.is_empty() && stdin_read_ready(&reader, 10) == Some(false) {
- if let Some(data) = crate::raw_input::flush_incomplete_input_bytes(&mut buffer)
- {
+ if stdin_read_ready(&reader, 10) == Some(false) {
+ for data in framer.flush_timeout() {
if event_tx
.blocking_send(ClientLoopEvent::StdinInput(data))
.is_err()