diff options
Diffstat (limited to 'src/client/frame_output.rs')
| -rw-r--r-- | src/client/frame_output.rs | 79 |
1 files changed, 77 insertions, 2 deletions
diff --git a/src/client/frame_output.rs b/src/client/frame_output.rs index 5d12c78c..d9e03f14 100644 --- a/src/client/frame_output.rs +++ b/src/client/frame_output.rs @@ -1,11 +1,86 @@ use std::collections::HashSet; -use std::io; +use std::io::{self, Write as _}; use std::sync::{Mutex, OnceLock}; -use crate::protocol::render_ansi; +use crate::kitty_graphics::{GraphicsOperation, GraphicsOutput}; +use crate::protocol::{render_ansi, FrameData}; +use base64::Engine as _; + +/// Local output only; raw uploads are never added to a published frame codec. +#[derive(Debug)] +pub(crate) struct ComposedFrame { + pub(crate) frame: FrameData, + pub(crate) graphics: GraphicsOutput, +} + +impl From<FrameData> for ComposedFrame { + fn from(mut frame: FrameData) -> Self { + let graphics = GraphicsOutput::from_bytes(std::mem::take(&mut frame.graphics)); + Self { frame, graphics } + } +} + +impl std::ops::Deref for ComposedFrame { + type Target = FrameData; + + fn deref(&self) -> &Self::Target { + &self.frame + } +} static RECEIVED_KITTY_GRAPHICS_IDS: OnceLock<Mutex<HashSet<u32>>> = OnceLock::new(); +pub(super) fn write_composed_frame( + mut writer: impl io::Write, + encoded: &[u8], + graphics: &GraphicsOutput, + files: &mut super::image_files::FileTransport, +) -> io::Result<()> { + if graphics.is_empty() { + return writer.write_all(encoded); + } + let mut writer = io::BufWriter::with_capacity(64 * 1024, writer); + let insertion = render_ansi::final_sync_output_end(encoded).unwrap_or(encoded.len()); + writer.write_all(&encoded[..insertion])?; + writer.write_all(b"\x1b7")?; + for operation in &graphics.operations { + match operation { + GraphicsOperation::Bytes(bytes) => { + record_received_kitty_graphics(bytes); + writer.write_all(bytes)?; + } + GraphicsOperation::Upload { control, data } => { + let file_eligible = control + .split(',') + .any(|part| matches!(part, "f=24" | "f=32" | "f=100")); + if file_eligible { + if let Some(path) = files + .probe() + .and_then(|path| path.to_str().map(str::to_owned)) + { + let path = + base64::engine::general_purpose::STANDARD.encode(path.as_bytes()); + write!(writer, "\x1b_Ga=q,t=t,f=32,s=1,v=1,i=1,q=2;{path}\x1b\\")?; + } + } + let path = file_eligible.then(|| files.prepare(data)).flatten(); + let header = format!("\x1b_G{control};\x1b\\"); + record_received_kitty_graphics(header.as_bytes()); + if let Some(path) = path.as_ref().and_then(|path| path.to_str()) { + let control = control.replace(",t=d,", ",t=t,"); + let path = base64::engine::general_purpose::STANDARD.encode(path.as_bytes()); + write!(writer, "\x1b_G{control};{path}\x1b\\")?; + } else { + crate::kitty_graphics::write_kitty_data(&mut writer, control, data)?; + } + } + } + } + writer.write_all(b"\x1b8")?; + writer.write_all(&encoded[insertion..])?; + io::Write::flush(&mut writer) +} + pub(super) fn write_encoded_frame_with_graphics( mut writer: impl io::Write, encoded: &[u8], |
