aboutsummaryrefslogtreecommitdiff
path: root/src/app/api/pane_graphics.rs
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-08-13 17:21:26 +0300
committerGitHub <noreply@github.com>2026-08-13 17:21:26 +0300
commitb7dca4bb2a2cf488407ae051b40782f03fcabc62 (patch)
tree12ba75991215634676b49f0feb6eafa1d31ac230 /src/app/api/pane_graphics.rs
parent952729ee03e0939d7a9d893f87f24179cf0eb7cb (diff)
fix(graphics): support high-dpi direct file frames (#2680)
Diffstat (limited to 'src/app/api/pane_graphics.rs')
-rw-r--r--src/app/api/pane_graphics.rs86
1 files changed, 79 insertions, 7 deletions
diff --git a/src/app/api/pane_graphics.rs b/src/app/api/pane_graphics.rs
index f5f5f2b7..4fc1deec 100644
--- a/src/app/api/pane_graphics.rs
+++ b/src/app/api/pane_graphics.rs
@@ -2,8 +2,8 @@ use base64::Engine;
use crate::api::schema::{
PaneGraphicsClearParams, PaneGraphicsSetParams, PaneGraphicsStreamParams, ResponseResult,
- PANE_GRAPHICS_MAX_LAYERS_PER_PANE, PANE_GRAPHICS_PRIMARY_LAYER_ID, PANE_GRAPHICS_SET_MAX_BYTES,
- PANE_GRAPHICS_STREAM_MAX_BYTES,
+ PANE_GRAPHICS_DIRECT_FILE_MAX_BYTES, PANE_GRAPHICS_MAX_LAYERS_PER_PANE,
+ PANE_GRAPHICS_PRIMARY_LAYER_ID, PANE_GRAPHICS_SET_MAX_BYTES, PANE_GRAPHICS_STREAM_MAX_BYTES,
};
use crate::app::pane_graphics::{Key as PaneGraphicsKey, Layer, Slot};
use crate::app::App;
@@ -65,6 +65,7 @@ impl App {
Vec::new()
},
file_frame_max_bytes: direct.then_some(PANE_GRAPHICS_STREAM_MAX_BYTES),
+ file_frame_direct_max_bytes: direct.then_some(PANE_GRAPHICS_DIRECT_FILE_MAX_BYTES),
// Damage metadata never changes the complete canonical frame contract.
file_frame_damage: true,
max_layers_per_pane: PANE_GRAPHICS_MAX_LAYERS_PER_PANE,
@@ -318,9 +319,18 @@ impl App {
) {
return encode_error(id, "invalid_image", "direct frames require rgba or bgra");
}
+ let primary = key.1 == PANE_GRAPHICS_PRIMARY_LAYER_ID;
+ let direct = self.direct_graphics_available
+ && primary
+ && params.format == crate::api::schema::PaneGraphicsFormat::Rgba;
+ let max_bytes = if direct {
+ PANE_GRAPHICS_DIRECT_FILE_MAX_BYTES
+ } else {
+ PANE_GRAPHICS_STREAM_MAX_BYTES
+ };
let expected_len =
match expected_len(params.format, params.image_width, params.image_height) {
- Ok(Some(len)) if len <= PANE_GRAPHICS_STREAM_MAX_BYTES => len,
+ Ok(Some(len)) if len <= max_bytes => len,
_ => return encode_error(id, "invalid_image", "invalid direct RGBA dimensions"),
};
let lease = match self
@@ -330,10 +340,6 @@ impl App {
Ok(lease) => lease,
Err(err) => return encode_error(id, "invalid_frame_file", err.to_string()),
};
- let primary = key.1 == PANE_GRAPHICS_PRIMARY_LAYER_ID;
- let direct = self.direct_graphics_available
- && primary
- && params.format == crate::api::schema::PaneGraphicsFormat::Rgba;
if !direct && !self.pane_graphics.can_store_inline(&key, expected_len) {
return encode_error(
id,
@@ -699,6 +705,14 @@ mod tests {
value["result"]["file_frame_formats"],
serde_json::json!(["rgba", "bgra"])
);
+ assert_eq!(
+ value["result"]["file_frame_max_bytes"],
+ PANE_GRAPHICS_STREAM_MAX_BYTES
+ );
+ assert_eq!(
+ value["result"]["file_frame_direct_max_bytes"],
+ PANE_GRAPHICS_DIRECT_FILE_MAX_BYTES
+ );
assert_eq!(value["result"]["file_frame_damage"], true);
assert_eq!(value["result"]["file_frame_transport"], "direct-kitty");
}
@@ -1039,6 +1053,24 @@ mod tests {
}
#[cfg(unix)]
+ fn sparse_direct_file(app: &App, name: &str, len: usize) -> String {
+ use std::os::unix::fs::OpenOptionsExt as _;
+ let path = app
+ .pane_graphics_files
+ .source_directory()
+ .unwrap()
+ .join(name);
+ let file = std::fs::OpenOptions::new()
+ .write(true)
+ .create_new(true)
+ .mode(0o600)
+ .open(&path)
+ .unwrap();
+ file.set_len(len as u64).unwrap();
+ path.to_string_lossy().into_owned()
+ }
+
+ #[cfg(unix)]
fn direct_params(
pane_id: String,
owner: &str,
@@ -1147,6 +1179,46 @@ mod tests {
#[cfg(unix)]
#[test]
+ fn direct_primary_rgba_accepts_fullscreen_retina_frame() {
+ let (mut app, pane_id) = app();
+ app.direct_graphics_available = true;
+ app.handle_pane_graphics_stream_open(
+ "open".into(),
+ PaneGraphicsStreamParams {
+ pane_id: pane_id.clone(),
+ layer_id: None,
+ z_index: 0,
+ owner: "owner".into(),
+ },
+ );
+ let (image_width, image_height) = (3456, 2234);
+ let len = image_width * image_height * 4;
+ let path = sparse_direct_file(&app, "retina-frame", len as usize);
+ let response = app.handle_pane_graphics_stream_direct(
+ "frame".into(),
+ crate::api::schema::PaneGraphicsDirectParams {
+ image_width,
+ image_height,
+ ..direct_params(pane_id, "owner", path)
+ },
+ );
+
+ assert!(serde_json::from_str::<SuccessResponse>(&response).is_ok());
+ assert!(app
+ .pane_graphics
+ .slots
+ .values()
+ .next()
+ .unwrap()
+ .layer
+ .as_ref()
+ .unwrap()
+ .direct_lease()
+ .is_some());
+ }
+
+ #[cfg(unix)]
+ #[test]
fn bgra_and_secondary_file_frames_are_canonical_owned_rgba() {
let (mut app, pane_id) = app();
app.direct_graphics_available = true;