From 4600309051e5d6bea1f64e72cec585b102117ce5 Mon Sep 17 00:00:00 2001 From: Ogulcan Celik Date: Wed, 13 May 2026 14:14:40 +0300 Subject: feat: support kitty graphics in attached clients --- src/server/render_stream.rs | 49 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 8 deletions(-) (limited to 'src/server/render_stream.rs') diff --git a/src/server/render_stream.rs b/src/server/render_stream.rs index 45542a7a..ca0ecddd 100644 --- a/src/server/render_stream.rs +++ b/src/server/render_stream.rs @@ -57,7 +57,8 @@ impl ClientRenderState { if blit_encoder.is_current(frame) { return None; } - let encoded = blit_encoder.encode(frame, false); + let mut encoded = blit_encoder.encode(frame, false); + insert_graphics_before_sync_end(&mut encoded.bytes, &frame.graphics); Some(PreparedRender { message: ServerMessage::Terminal(TerminalFrame { seq: *seq + 1, @@ -72,12 +73,6 @@ impl ClientRenderState { } } - pub(crate) fn note_oversized_frame(&mut self, frame: FrameData) { - if let Self::Semantic { last_frame } = self { - *last_frame = Some(frame); - } - } - pub(crate) fn commit_sent_frame(&mut self, frame: FrameData, prepared: PreparedRender) { match (self, prepared.encoded) { (Self::Semantic { last_frame }, None) => *last_frame = Some(frame), @@ -98,6 +93,30 @@ impl ClientRenderState { } } +const SYNC_OUTPUT_END: &[u8] = b"\x1b[?2026l"; + +fn insert_graphics_before_sync_end(encoded: &mut Vec, graphics: &[u8]) { + if graphics.is_empty() { + return; + } + + if let Some(sync_end) = rfind_subslice(encoded, SYNC_OUTPUT_END) { + encoded.splice(sync_end..sync_end, graphics.iter().copied()); + } else { + encoded.extend_from_slice(graphics); + } +} + +fn rfind_subslice(haystack: &[u8], needle: &[u8]) -> Option { + if needle.is_empty() || needle.len() > haystack.len() { + return None; + } + + haystack + .windows(needle.len()) + .rposition(|window| window == needle) +} + /// A prepared client render message plus any baseline state needed after send. pub(crate) struct PreparedRender { message: ServerMessage, @@ -201,9 +220,23 @@ pub(crate) fn render_virtual( app_state: &mut AppState, area: Rect, resize_panes: bool, +) -> (ratatui::buffer::Buffer, Option) { + render_virtual_with_cell_size( + app_state, + area, + resize_panes, + crate::kitty_graphics::HostCellSize::default(), + ) +} + +pub(crate) fn render_virtual_with_cell_size( + app_state: &mut AppState, + area: Rect, + resize_panes: bool, + cell_size: crate::kitty_graphics::HostCellSize, ) -> (ratatui::buffer::Buffer, Option) { if resize_panes { - crate::ui::compute_view(app_state, area); + crate::ui::compute_view_with_cell_size(app_state, area, cell_size); } else { crate::ui::compute_view_without_resizing_panes(app_state, area); } -- cgit v1.2.3