diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-09-13 02:40:13 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-09-13 02:40:13 +0300 |
| commit | 98a8c6ce52a054b582a6210c79d76e10a99ccccc (patch) | |
| tree | 786afb220da4c4f89d05db0b15735e8b231f7737 /src | |
| parent | 5eafc612674c4ac8f7c938769159a3cfd22e52d4 (diff) | |
fix: restore alternate-screen pane width (#4016)
refs #3329
Diffstat (limited to 'src')
| -rw-r--r-- | src/pane/terminal.rs | 17 | ||||
| -rw-r--r-- | src/server/headless/render.rs | 55 | ||||
| -rw-r--r-- | src/server/headless/tests/mod.rs | 28 |
3 files changed, 100 insertions, 0 deletions
diff --git a/src/pane/terminal.rs b/src/pane/terminal.rs index b201702b..dfd4f536 100644 --- a/src/pane/terminal.rs +++ b/src/pane/terminal.rs @@ -5607,6 +5607,23 @@ mod tests { } #[test] + fn enabling_in_band_size_reports_after_alt_screen_resize_reports_current_size() { + let (tx, _rx) = mpsc::channel(4); + let terminal = crate::ghostty::Terminal::new(91, 24, 0).unwrap(); + let pane = GhosttyPaneTerminal::new(terminal, tx.clone()).unwrap(); + let pane_id = PaneId::from_raw(1); + pane.process_pty_bytes(pane_id, 0, b"\x1b[?1049h", &tx); + assert!(pane.resize(24, 92, 9, 18).is_empty()); + + let result = pane.process_pty_bytes(pane_id, 0, b"\x1b[?2048h", &tx); + + assert_eq!( + result.terminal_responses, + vec![Bytes::from_static(b"\x1b[48;24;92;432;828t")] + ); + } + + #[test] fn resize_returns_in_band_size_report_response() { let (tx, _rx) = mpsc::channel(4); let mut terminal = crate::ghostty::Terminal::new(80, 24, 0).unwrap(); diff --git a/src/server/headless/render.rs b/src/server/headless/render.rs index 5e36be01..fc55417f 100644 --- a/src/server/headless/render.rs +++ b/src/server/headless/render.rs @@ -405,6 +405,61 @@ impl HeadlessServer { return; } + // Resize from the controlling client's geometry before drawing any observer. + // Retained updates fall back here when a pane changes alternate screens. + for (client_id, (cols, rows), cell_size, _, _) in &render_targets { + let Some(client) = self.clients.get(client_id) else { + continue; + }; + if !client.is_active_shell_client() { + continue; + } + let Some(tab_id) = self.shell_tab_id_for_client(*client_id) else { + continue; + }; + if self.tab_geometry_controllers.get(&tab_id) != Some(client_id) { + continue; + } + let changed = client + .render_state + .last_pane_surface() + .is_none_or(|surface| { + surface.panes.iter().any(|pane| { + let Some((workspace_index, pane_id)) = + self.app.parse_pane_id(&pane.pane_id) + else { + return false; + }; + self.app + .state + .runtime_for_pane_in_workspace( + &self.app.terminal_runtimes, + workspace_index, + pane_id, + ) + .is_some_and(|runtime| { + runtime.alternate_screen_active() != pane.alternate_screen_active + }) + }) + }); + if changed { + if let Some(target) = self.shell_target_for_client(*client_id) { + crate::ui::resize_tab_surface( + &self.app.state, + &self.app.terminal_runtimes, + target.workspace_index, + target.tab_index, + Rect::new(0, 0, *cols, *rows), + if cell_size.is_known() { + *cell_size + } else { + crate::kitty_graphics::HostCellSize::default() + }, + ); + } + } + } + let mut broken_clients: Vec<u64> = Vec::new(); for (client_id, (cols, rows), cell_size, _is_foreground, mode) in render_targets { let area = Rect::new(0, 0, cols, rows); diff --git a/src/server/headless/tests/mod.rs b/src/server/headless/tests/mod.rs index d666ae22..2c13dc8b 100644 --- a/src/server/headless/tests/mod.rs +++ b/src/server/headless/tests/mod.rs @@ -1057,6 +1057,25 @@ async fn retained_snapshot_survives_a_writer_waiting_for_the_terminal_core() { } #[tokio::test] +async fn first_shell_surface_resizes_a_pane_that_entered_alternate_screen() { + let mut server = test_headless_server(); + let pane_id = install_shared_view_test_runtime(&mut server); + let (_control, render) = connect_test_shell(&mut server, 7, 80, 23); + let initial_size = server.app.state.workspaces[0].test_runtimes[&pane_id].current_size(); + + write_shared_test_pane(&mut server, pane_id, b"\x1b[?1049hALT"); + server.render_and_stream(); + + let surface = recv_pane_surface(&render, "first alternate-screen surface"); + assert!(surface.panes[0].alternate_screen_active); + assert_eq!( + server.app.state.workspaces[0].test_runtimes[&pane_id].current_size(), + (initial_size.0, initial_size.1 + 1) + ); + shutdown_test_runtimes(&mut server); +} + +#[tokio::test] async fn different_size_shells_receive_geometry_specific_patches_from_one_dirty_collection() { let mut server = test_headless_server(); let pane_id = install_shared_view_test_runtime(&mut server); @@ -1067,6 +1086,7 @@ async fn different_size_shells_receive_geometry_specific_patches_from_one_dirty_ server.render_and_stream(); let large_initial = recv_pane_surface(&large_render, "large initial surface"); let small_initial = recv_pane_surface(&small_render, "small initial surface"); + let initial_size = server.app.state.workspaces[0].test_runtimes[&pane_id].current_size(); assert_eq!( (large_initial.frame.width, large_initial.frame.height), (80, 23) @@ -1135,6 +1155,10 @@ async fn different_size_shells_receive_geometry_specific_patches_from_one_dirty_ assert!(large_alt.panes[0].alternate_screen_active); assert!(small_alt.panes[0].alternate_screen_active); assert_eq!( + server.app.state.workspaces[0].test_runtimes[&pane_id].current_size(), + (initial_size.0, initial_size.1 + 1) + ); + assert_eq!( large_alt.panes[0].inner_rect.width, large_initial.panes[0].inner_rect.width + 1 ); @@ -1151,6 +1175,10 @@ async fn different_size_shells_receive_geometry_specific_patches_from_one_dirty_ assert!(!large_main.panes[0].alternate_screen_active); assert!(!small_main.panes[0].alternate_screen_active); assert_eq!( + server.app.state.workspaces[0].test_runtimes[&pane_id].current_size(), + initial_size + ); + assert_eq!( large_main.panes[0].inner_rect, large_initial.panes[0].inner_rect ); |
