aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-09-13 02:40:13 +0300
committerGitHub <noreply@github.com>2026-09-13 02:40:13 +0300
commit98a8c6ce52a054b582a6210c79d76e10a99ccccc (patch)
tree786afb220da4c4f89d05db0b15735e8b231f7737 /src
parent5eafc612674c4ac8f7c938769159a3cfd22e52d4 (diff)
fix: restore alternate-screen pane width (#4016)
refs #3329
Diffstat (limited to 'src')
-rw-r--r--src/pane/terminal.rs17
-rw-r--r--src/server/headless/render.rs55
-rw-r--r--src/server/headless/tests/mod.rs28
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
);