diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-04 22:43:06 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-04 22:43:06 +0300 |
| commit | 670656b26f1aeadc3f18e6692a9fdf1b02da799c (patch) | |
| tree | 7401a16884f8c4088bff920435dce1e0f7b401de /src | |
| parent | 648afa1fd5b4c2eb74b4292b4eceeeb6d93016c1 (diff) | |
fix: forward resize terminal responses
refs #471
Diffstat (limited to 'src')
| -rw-r--r-- | src/pane.rs | 28 | ||||
| -rw-r--r-- | src/pane/terminal.rs | 37 | ||||
| -rw-r--r-- | src/pty/actor.rs | 79 |
3 files changed, 116 insertions, 28 deletions
diff --git a/src/pane.rs b/src/pane.rs index 248f5bb1..19556ffe 100644 --- a/src/pane.rs +++ b/src/pane.rs @@ -722,11 +722,24 @@ impl PaneRuntimeIo { } } - fn resize(&self, rows: u16, cols: u16, cell_width_px: u32, cell_height_px: u32) { + fn resize( + &self, + rows: u16, + cols: u16, + cell_width_px: u32, + cell_height_px: u32, + terminal_responses: Vec<Bytes>, + ) { match self { #[cfg(unix)] PaneRuntimeIo::Actor(actor) => { - actor.resize(rows, cols, cell_width_px, cell_height_px); + actor.resize( + rows, + cols, + cell_width_px, + cell_height_px, + terminal_responses, + ); } #[cfg(test)] PaneRuntimeIo::TestChannel { resize_tx, .. } => { @@ -1814,9 +1827,16 @@ impl PaneRuntime { return; } self.current_size.set(size); - self.terminal + let terminal_responses = self + .terminal .resize(rows, cols, cell_width_px, cell_height_px); - self.io.resize(rows, cols, cell_width_px, cell_height_px); + self.io.resize( + rows, + cols, + cell_width_px, + cell_height_px, + terminal_responses, + ); } pub fn nudge_child_redraw_after_handoff(&self) { diff --git a/src/pane/terminal.rs b/src/pane/terminal.rs index 88924d36..f2fceaa8 100644 --- a/src/pane/terminal.rs +++ b/src/pane/terminal.rs @@ -146,9 +146,15 @@ impl PaneTerminal { .process_pty_bytes(pane_id, shell_pid, bytes, response_writer) } - pub fn resize(&self, rows: u16, cols: u16, cell_width_px: u32, cell_height_px: u32) { + pub fn resize( + &self, + rows: u16, + cols: u16, + cell_width_px: u32, + cell_height_px: u32, + ) -> Vec<Bytes> { self.ghostty - .resize(rows, cols, cell_width_px, cell_height_px); + .resize(rows, cols, cell_width_px, cell_height_px) } pub fn scroll_up(&self, lines: usize) { @@ -662,7 +668,13 @@ impl GhosttyPaneTerminal { } } - pub fn resize(&self, rows: u16, cols: u16, cell_width_px: u32, cell_height_px: u32) { + pub fn resize( + &self, + rows: u16, + cols: u16, + cell_width_px: u32, + cell_height_px: u32, + ) -> Vec<Bytes> { if let Ok(mut core) = self.core.lock() { let offset_from_bottom = core .terminal @@ -694,6 +706,7 @@ impl GhosttyPaneTerminal { let _ = core .terminal .resize(cols, rows, cell_width_px, cell_height_px); + let terminal_responses = self.drain_pending_pty_responses(); let bottom_is_blank = ghostty_detection_text(&core) .map(|text| text.trim().is_empty()) @@ -716,6 +729,9 @@ impl GhosttyPaneTerminal { remaining -= 1; } } + terminal_responses + } else { + Vec::new() } } @@ -2622,6 +2638,21 @@ mod tests { } #[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(); + terminal.mode_set(2048, true).unwrap(); + let pane = GhosttyPaneTerminal::new(terminal, tx).unwrap(); + + let responses = pane.resize(40, 100, 9, 18); + + assert_eq!( + responses, + vec![Bytes::from_static(b"\x1B[48;40;100;720;900t")] + ); + } + + #[test] fn synchronized_output_suppresses_intermediate_render_requests_until_batch_ends() { let (tx, _rx) = mpsc::channel(4); let terminal = crate::ghostty::Terminal::new(80, 24, 0).unwrap(); diff --git a/src/pty/actor.rs b/src/pty/actor.rs index cce58a75..70634475 100644 --- a/src/pty/actor.rs +++ b/src/pty/actor.rs @@ -50,9 +50,15 @@ struct PtyResize { cell_height_px: u32, } +#[derive(Debug, Clone, PartialEq, Eq)] +struct PtyResizeRequest { + resize: PtyResize, + terminal_responses: Vec<Bytes>, +} + #[derive(Default)] struct SharedPtyControls { - resize: Option<PtyResize>, + resize: Option<PtyResizeRequest>, nudge: Option<PtyResize>, } @@ -151,17 +157,27 @@ impl PtyIoActorHandle { } } - pub(crate) fn resize(&self, rows: u16, cols: u16, cell_width_px: u32, cell_height_px: u32) { + pub(crate) fn resize( + &self, + rows: u16, + cols: u16, + cell_width_px: u32, + cell_height_px: u32, + terminal_responses: Vec<Bytes>, + ) { { let mut controls = self .controls .lock() .unwrap_or_else(|poisoned| poisoned.into_inner()); - controls.resize = Some(PtyResize { - rows, - cols, - cell_width_px, - cell_height_px, + controls.resize = Some(PtyResizeRequest { + resize: PtyResize { + rows, + cols, + cell_width_px, + cell_height_px, + }, + terminal_responses, }); } self.wake_actor(); @@ -592,8 +608,9 @@ impl PtyIoActorRunner { if self.state == ActorState::Released { return; } - if let Some(resize) = resize { - self.resize(resize); + if let Some(request) = resize { + self.resize(request.resize); + self.enqueue_terminal_responses(request.terminal_responses); } if let Some(nudge) = nudge { self.nudge(nudge); @@ -612,16 +629,19 @@ impl PtyIoActorRunner { } Ok(n) => { let result = (self.on_read)(&buf[..n]); - for response in result.terminal_responses { - if self.state != ActorState::Released { - self.pending_writes.push_back(response); - } - } + self.enqueue_terminal_responses(result.terminal_responses); true } } } + fn enqueue_terminal_responses(&mut self, terminal_responses: Vec<Bytes>) { + if self.state == ActorState::Released { + return; + } + self.pending_writes.extend(terminal_responses); + } + fn flush_pending_writes_once(&mut self) { while let Some(bytes) = self.pending_writes.front() { let chunk = &bytes[self.current_write_offset..]; @@ -922,18 +942,21 @@ mod tests { controls: Arc::clone(&controls), }; - handle.resize(20, 80, 8, 16); - handle.resize(40, 120, 9, 18); + handle.resize(20, 80, 8, 16, vec![Bytes::from_static(b"old")]); + handle.resize(40, 120, 9, 18, vec![Bytes::from_static(b"new")]); handle.nudge_child_redraw_after_handoff(41, 121, 10, 20); let controls = controls.lock().expect("controls lock"); assert_eq!( controls.resize, - Some(PtyResize { - rows: 40, - cols: 120, - cell_width_px: 9, - cell_height_px: 18, + Some(PtyResizeRequest { + resize: PtyResize { + rows: 40, + cols: 120, + cell_width_px: 9, + cell_height_px: 18, + }, + terminal_responses: vec![Bytes::from_static(b"new")], }) ); assert_eq!( @@ -947,6 +970,20 @@ mod tests { ); } + #[test] + fn resize_writes_terminal_responses_after_applying_resize() { + let (handle, mut peer, _read_rx) = actor_with_socket_pair(false); + let response = Bytes::from_static(b"\x1B[48;40;100;720;900t"); + + handle.resize(40, 100, 9, 18, vec![response.clone()]); + + let mut buf = vec![0; response.len()]; + peer.read_exact(&mut buf) + .expect("peer receives resize response"); + assert_eq!(Bytes::from(buf), response); + handle.shutdown(); + } + #[tokio::test] async fn async_user_input_waits_for_queue_capacity() { let (data_tx, mut data_rx) = mpsc::channel(1); |
