aboutsummaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-05-15 21:17:55 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-05-15 21:17:55 +0300
commite1a84a8e340f09b9479ca2df333ba135a45d2048 (patch)
tree23ebb0b08e3531a03373d0586372b07e93dd0dc8 /src/server
parent73dba97521f5a0e3104518ef48b775a178998f52 (diff)
fix: force redraw on terminal focus gain
Diffstat (limited to 'src/server')
-rw-r--r--src/server/headless.rs67
1 files changed, 60 insertions, 7 deletions
diff --git a/src/server/headless.rs b/src/server/headless.rs
index 9bb3181f..29c8de6e 100644
--- a/src/server/headless.rs
+++ b/src/server/headless.rs
@@ -244,6 +244,14 @@ impl ClientConnection {
writer,
}
}
+
+ fn request_full_redraw(&mut self) {
+ self.render_state.reset_baseline();
+ }
+
+ fn request_semantic_redraw_after_input(&mut self) {
+ self.render_state.reset_semantic_input_baseline();
+ }
}
// ---------------------------------------------------------------------------
@@ -529,7 +537,7 @@ impl HeadlessServer {
// rendering semantics. Force one fresh frame to every remaining client
// even if the next rendered buffer compares equal to its cached frame.
for client in self.clients.values_mut() {
- client.render_state.reset_baseline();
+ client.request_full_redraw();
}
}
@@ -1154,14 +1162,20 @@ impl HeadlessServer {
}
ServerEvent::ClientInput { client_id, data } => {
debug!(client_id, len = data.len(), "client input received");
+ let events = crate::raw_input::parse_raw_input_bytes_sync(&data);
+ let host_surface_redraw =
+ crate::raw_input::events_require_host_surface_redraw(&events);
if let Some(client) = self.clients.get_mut(&client_id) {
- // Ensure semantic clients receive one post-input frame even if the
- // semantic buffer compares equal. Terminal-ANSI clients must keep their
- // server-side blit baseline; resetting it here forces a full redraw on
- // every keypress and makes remote sessions feel extremely slow.
- client.render_state.reset_semantic_input_baseline();
+ if host_surface_redraw {
+ client.request_full_redraw();
+ } else {
+ // Ensure semantic clients receive one post-input frame even if the
+ // semantic buffer compares equal. Terminal-ANSI clients must keep their
+ // server-side blit baseline; resetting it here forces a full redraw on
+ // every keypress and makes remote sessions feel extremely slow.
+ client.request_semantic_redraw_after_input();
+ }
}
- let events = crate::raw_input::parse_raw_input_bytes_sync(&data);
self.update_client_outer_focus_from_events(client_id, &events);
let interaction = Self::events_include_interaction(&events);
let foreground_changed = if interaction {
@@ -2620,6 +2634,45 @@ mod tests {
}
#[test]
+ fn outer_focus_gained_forces_terminal_ansi_full_redraw() {
+ let mut server = test_headless_server();
+ let (client_tx, _client_control_rx, client_rx) = test_client_writer();
+
+ server.clients.insert(
+ 1,
+ ClientConnection::new(
+ (80, 24),
+ crate::kitty_graphics::HostCellSize::default(),
+ crate::terminal_theme::TerminalTheme::default(),
+ None,
+ 1,
+ RenderEncoding::TerminalAnsi,
+ Some(client_tx),
+ ),
+ );
+ server.foreground_client_id = Some(1);
+
+ server.render_and_stream();
+ let _ = client_rx
+ .recv_timeout(Duration::from_millis(100))
+ .expect("initial terminal frame");
+
+ assert!(server.handle_server_event(ServerEvent::ClientInput {
+ client_id: 1,
+ data: b"\x1b[I".to_vec(),
+ }));
+ server.render_and_stream();
+
+ match read_server_message(client_rx.recv_timeout(Duration::from_millis(100)).unwrap()) {
+ ServerMessage::Terminal(frame) => {
+ assert_eq!(frame.seq, 2);
+ assert!(frame.full);
+ }
+ other => panic!("expected terminal frame, got {other:?}"),
+ }
+ }
+
+ #[test]
fn full_render_queue_does_not_advance_terminal_ansi_baseline() {
let mut server = test_headless_server();
let (client_tx, _client_control_rx, client_rx) = test_client_writer();