aboutsummaryrefslogtreecommitdiff
path: root/src/client/frame_output.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/frame_output.rs')
-rw-r--r--src/client/frame_output.rs79
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],