aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-06-05 22:03:01 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-06-05 22:04:53 +0300
commit658017de68b2ab2bced9c300ae8b6968fae3e998 (patch)
treeab820ca1779ceb42dc5b7a1b0343626b8a96f6a3 /src
parent0f92b2f02752fcdff099dae72915cd1cc85f7fc4 (diff)
fix: keep focus redraw pending until sent
Diffstat (limited to 'src')
-rw-r--r--src/server/headless.rs88
1 files changed, 88 insertions, 0 deletions
diff --git a/src/server/headless.rs b/src/server/headless.rs
index 11e5f6b8..c1fc7352 100644
--- a/src/server/headless.rs
+++ b/src/server/headless.rs
@@ -1929,6 +1929,7 @@ impl HeadlessServer {
if let Some(client) = self.clients.get_mut(&client_id) {
if host_surface_redraw {
client.request_full_redraw();
+ client.render_pending = true;
} else {
// Ensure semantic clients receive one post-input frame even if the
// semantic buffer compares equal. Terminal-ANSI clients must keep their
@@ -5376,6 +5377,62 @@ next_tab = ""
}
}
+ #[tokio::test]
+ async fn outer_focus_gained_client_render_pending_survives_semantic_render_queue_full() {
+ let (mut server, client_rx, pane_id) = retained_test_server(b"aaaa");
+
+ server.render_and_stream();
+ let _ = client_rx
+ .recv_timeout(Duration::from_millis(100))
+ .expect("initial semantic frame");
+
+ let queued = HeadlessServer::frame_server_message(&ServerMessage::ReloadSoundConfig)
+ .expect("serialize dummy message");
+ server
+ .clients
+ .get(&1)
+ .unwrap()
+ .writer
+ .as_ref()
+ .unwrap()
+ .render
+ .send(queued)
+ .expect("pre-fill render queue");
+
+ assert!(server.handle_server_event(ServerEvent::ClientInput {
+ client_id: 1,
+ data: b"\x1b[I".to_vec(),
+ }));
+ assert!(server.clients.get(&1).unwrap().render_pending);
+
+ server.render_and_stream();
+
+ assert!(server.clients.get(&1).unwrap().render_pending);
+ assert!(matches!(
+ read_server_message(client_rx.recv_timeout(Duration::from_millis(100)).unwrap()),
+ ServerMessage::ReloadSoundConfig
+ ));
+
+ let runtime = server
+ .app
+ .state
+ .runtime_for_pane_in_workspace(&server.app.terminal_runtimes, 0, pane_id)
+ .expect("runtime");
+ runtime.test_process_pty_bytes(b"\rZ");
+
+ assert!(!server.render_retained_pty_update_and_stream());
+ assert!(client_rx.recv_timeout(Duration::from_millis(50)).is_err());
+
+ assert!(server.handle_server_event(ServerEvent::ClientWriterDrained { client_id: 1 }));
+ server.render_and_stream();
+
+ assert!(!server.clients.get(&1).unwrap().render_pending);
+ assert!(matches!(
+ read_server_message(client_rx.recv_timeout(Duration::from_millis(100)).unwrap()),
+ ServerMessage::Frame(_)
+ ));
+ }
+
#[test]
fn outer_focus_gained_does_not_force_terminal_ansi_full_redraw_when_disabled() {
let mut server = test_headless_server();
@@ -5423,6 +5480,37 @@ next_tab = ""
}
#[test]
+ fn outer_focus_gained_does_not_mark_semantic_render_pending_when_disabled() {
+ let mut server = test_headless_server();
+ server.app.state.redraw_on_focus_gained = false;
+ 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::SemanticFrame,
+ Some(client_tx),
+ ),
+ );
+ server.foreground_client_id = Some(1);
+
+ assert!(server.handle_server_event(ServerEvent::ClientInput {
+ client_id: 1,
+ data: b"\x1b[I".to_vec(),
+ }));
+
+ assert!(!server.clients.get(&1).unwrap().render_pending);
+ assert!(!server.app.full_redraw_pending);
+ assert_eq!(server.clients[&1].outer_terminal_focus, Some(true));
+ assert_eq!(server.app.state.outer_terminal_focus, Some(true));
+ }
+
+ #[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();