mirror of
https://github.com/zellij-org/zellij.git
synced 2026-10-11 22:50:07 +00:00
fix(kitty-graphics): zlib-compress images forwarded to the host terminal (#5608)
* fix(kitty-graphics): zlib-compress images forwarded to the host terminal `emit_kitty_transmit` sent every image to the host as uncompressed RGBA base64 (`f=32,t=d`), about 20x the size of a PNG the application sent, and re-sent it on every tab switch, pane split and resize step. Compress the cached host payload with zlib and tag the transmit with `o=z`, which the kitty protocol defines as part of its base feature set. Measured with a headless client at 429x77 (8x17 px cells): a 121 KB PNG went from 2.4 MB to 163 KB on the wire, a 943 KB PNG from 19.9 MB to 1.9 MB, with no measurable change in server CPU. Fixes #5596 * fix: conditional zlib compression depending on outer terminal support * rustfmt * add pr --------- Co-authored-by: Aram Drevekenin <[email protected]>
This commit is contained in:
co-authored by
Aram Drevekenin
parent
3e470a43dc
commit
2fce032cce
@@ -24,4 +24,5 @@ vte = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
base64 = { version = "0.22", default-features = false, features = ["std"] }
|
||||
flate2 = { version = "1.1.9", default-features = false, features = ["rust_backend"] }
|
||||
tempfile = { workspace = true }
|
||||
|
||||
@@ -28,6 +28,7 @@ struct ReceivedBytesWithChangeSignal {
|
||||
pub enum HostTerminal {
|
||||
Basic,
|
||||
Kitty,
|
||||
KittyZlib,
|
||||
Manual,
|
||||
}
|
||||
|
||||
@@ -35,6 +36,8 @@ const PRIMARY_DA_QUERY: &[u8] = b"\x1b[c";
|
||||
const PRIMARY_DA_REPLY: &[u8] = b"\x1b[?62;22c";
|
||||
const KITTY_GRAPHICS_PROBE_PREFIX: &[u8] = b"\x1b_Ga=q,i=31,";
|
||||
const KITTY_GRAPHICS_PROBE_REPLY: &[u8] = b"\x1b_Gi=31;OK\x1b\\";
|
||||
const KITTY_ZLIB_PROBE_PREFIX: &[u8] = b"\x1b_Ga=q,i=32,";
|
||||
const KITTY_ZLIB_PROBE_REPLY: &[u8] = b"\x1b_Gi=32;OK\x1b\\";
|
||||
const CELL_SIZE_QUERY: &[u8] = b"\x1b[16t";
|
||||
const CELL_SIZE_REPLY: &[u8] = b"\x1b[6;21;8t";
|
||||
|
||||
@@ -53,6 +56,12 @@ impl HostResponder {
|
||||
(KITTY_GRAPHICS_PROBE_PREFIX, KITTY_GRAPHICS_PROBE_REPLY),
|
||||
(CELL_SIZE_QUERY, CELL_SIZE_REPLY),
|
||||
],
|
||||
HostTerminal::KittyZlib => &[
|
||||
(PRIMARY_DA_QUERY, PRIMARY_DA_REPLY),
|
||||
(KITTY_GRAPHICS_PROBE_PREFIX, KITTY_GRAPHICS_PROBE_REPLY),
|
||||
(KITTY_ZLIB_PROBE_PREFIX, KITTY_ZLIB_PROBE_REPLY),
|
||||
(CELL_SIZE_QUERY, CELL_SIZE_REPLY),
|
||||
],
|
||||
HostTerminal::Manual => &[],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,12 @@ fn start_zellij() -> TestSession {
|
||||
.start()
|
||||
}
|
||||
|
||||
fn start_zellij_with_a_zlib_capable_host() -> TestSession {
|
||||
TestRunner::new(TERMINAL_SIZE)
|
||||
.with_host_terminal(HostTerminal::KittyZlib)
|
||||
.start()
|
||||
}
|
||||
|
||||
fn setup_kitty_host(zellij: &TestSession, terminal: &FakePtyHandle) {
|
||||
terminal.disable_echo();
|
||||
zellij.send_stdin(b"Z");
|
||||
@@ -107,6 +113,41 @@ fn pane_kitty_image_reaches_client_with_transmit_and_placement() {
|
||||
zellij.quit();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kitty_image_is_zlib_compressed_when_the_host_confirms_zlib_support() {
|
||||
use base64::Engine as _;
|
||||
use std::io::Read;
|
||||
|
||||
let mut zellij = start_zellij_with_a_zlib_capable_host();
|
||||
let terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
|
||||
setup_kitty_host(&zellij, &terminal);
|
||||
|
||||
terminal.output(RGB_2X2_A_T);
|
||||
|
||||
let compressed_header: &[u8] = b"\x1b_Ga=t,q=2,f=32,o=z,t=d,i=2000000000,s=2,v=2,m=0;";
|
||||
let bytes = zellij.wait_until_raw_output(
|
||||
"compressed kitty transmit and placement reach the client",
|
||||
|bytes| {
|
||||
contains_bytes(bytes, compressed_header)
|
||||
&& contains_bytes(bytes, PLACEMENT)
|
||||
&& !contains_bytes(bytes, TRANSMIT_HEADER)
|
||||
},
|
||||
);
|
||||
|
||||
let payload_start = find_bytes(&bytes, compressed_header).unwrap() + compressed_header.len();
|
||||
let payload_len = find_bytes(&bytes[payload_start..], b"\x1b\\").unwrap();
|
||||
let compressed = base64::engine::general_purpose::STANDARD
|
||||
.decode(&bytes[payload_start..payload_start + payload_len])
|
||||
.unwrap();
|
||||
let mut inflated = Vec::new();
|
||||
flate2::read::ZlibDecoder::new(&compressed[..])
|
||||
.read_to_end(&mut inflated)
|
||||
.unwrap();
|
||||
assert_eq!(inflated, vec![0xffu8; 2 * 2 * 4]);
|
||||
|
||||
zellij.quit();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kitty_image_at_bottom_row_scrolls_and_reaches_client() {
|
||||
let mut zellij = start_zellij();
|
||||
|
||||
Reference in New Issue
Block a user