From 1777e9bba32b953ed1ad203b4a16d01105539000 Mon Sep 17 00:00:00 2001 From: Can Celik Date: Sun, 9 Aug 2026 02:04:39 +0300 Subject: feat(graphics): add direct pane frame streaming (#2523) * feat(graphics): add direct pane frame streaming * feat(graphics): expose pane visibility * test(graphics): preserve legacy terminal image output * fix(graphics): harden streaming lifecycle * fix(input): preserve mouse releases outside panes --- src/api/server.rs | 59 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 14 deletions(-) (limited to 'src/api/server.rs') diff --git a/src/api/server.rs b/src/api/server.rs index 3932b1ea..e8b73167 100644 --- a/src/api/server.rs +++ b/src/api/server.rs @@ -23,7 +23,6 @@ use crate::ipc::{ }; mod pane_graphics_stream; -pub(crate) use pane_graphics_stream::cancel_inactive_streams as cancel_inactive_pane_graphics_streams; const SOCKET_PERMISSION_MODE: u32 = 0o600; pub(super) const CONNECTION_POLL_INTERVAL: Duration = Duration::from_millis(100); @@ -332,12 +331,7 @@ fn handle_request( r#"{"id":"","error":{"code":"internal_error","message":"failed to encode response"}}"# .to_string() }), - _ => dispatch_to_app_with_timeout_and_write_completion( - request, - api_tx, - None, - response_write_complete, - ), + _ => dispatch_to_app(request, api_tx, None, response_write_complete, None), } } @@ -413,6 +407,7 @@ fn api_method_name(method: &Method) -> &'static str { Method::PaneGraphicsInfo(_) => "pane.graphics.info", Method::PaneGraphicsStream(_) => "pane.graphics.stream", Method::PaneGraphicsStreamSet(_) => "pane.graphics.stream.set", + Method::PaneGraphicsStreamDirect(_) => "pane.graphics.stream.direct", Method::PaneGraphicsStreamOpen(_) => "pane.graphics.stream.open", Method::PaneGraphicsStreamClose(_) => "pane.graphics.stream.close", Method::PaneReportAgent(_) => "pane.report_agent", @@ -749,22 +744,51 @@ pub(super) fn dispatch_to_app_with_timeout( api_tx: &ApiRequestSender, timeout: Option, ) -> String { - dispatch_to_app_with_timeout_and_write_completion(request, api_tx, timeout, None) + dispatch_to_app(request, api_tx, timeout, None, None) +} + +pub(super) fn dispatch_stream_open( + request: Request, + api_tx: &ApiRequestSender, + timeout: Duration, + active: Arc, +) -> String { + dispatch_to_app(request, api_tx, Some(timeout), None, Some(active)) } -fn dispatch_to_app_with_timeout_and_write_completion( +pub(super) fn dispatch_stream_frame( + request: Request, + api_tx: &ApiRequestSender, + active: Arc, +) -> String { + dispatch_to_app( + request, + api_tx, + Some(crate::app::pane_graphics::DIRECT_OUTER_TIMEOUT), + None, + Some(active), + ) +} + +fn dispatch_to_app( request: Request, api_tx: &ApiRequestSender, timeout: Option, response_write_complete: Option>, + stream_active: Option>, ) -> String { let request_id = request.id.clone(); + let request_active = stream_active.clone(); let (respond_to, response_rx) = std::sync::mpsc::channel(); if let Err(err) = api_tx.send(ApiRequestMessage { request, respond_to, response_write_complete, + stream_active, }) { + if let Some(active) = request_active { + active.store(false, Ordering::Release); + } return error_response_json( request_id, "server_unavailable", @@ -793,11 +817,16 @@ fn dispatch_to_app_with_timeout_and_write_completion( match response { Ok(response) => response, - Err(err) => error_response_json( - request_id, - "server_unavailable", - format!("request handling failed: {err}"), - ), + Err(err) => { + if let Some(active) = request_active { + active.store(false, Ordering::Release); + } + error_response_json( + request_id, + "server_unavailable", + format!("request handling failed: {err}"), + ) + } } } @@ -1335,6 +1364,8 @@ mod pane_graphics_request_tests { id: "graphics-max".into(), method: Method::PaneGraphicsSet(crate::api::schema::PaneGraphicsSetParams { pane_id: "pane_1".into(), + layer_id: None, + z_index: 0, owner: String::new(), format: crate::api::schema::PaneGraphicsFormat::Png, image_width: 1, -- cgit v1.2.3