Fix viewport dimensions in streaming status messages and screencast (#952)
## Summary Fixed an issue where custom viewport settings were ignored in WebSocket streaming, causing status messages to always report hardcoded dimensions (1280x720) instead of the actual viewport size. ## Changes Made - **Added viewport tracking to StreamServer**: New `viewport_width` and `viewport_height` fields to store current dimensions - **Updated viewport synchronization**: Modified `handle_viewport()` and `handle_device()` to update the stream server when viewport changes - **Fixed status message broadcasting**: Replaced hardcoded dimensions with actual viewport values in `broadcast_status()` calls - **Improved screencast defaults**: Changed screencast to use stored viewport dimensions as defaults instead of hardcoded 1280x720 - **Added viewport getter methods**: New `set_viewport()` and `viewport()` methods on StreamServer for dimension management This ensures that WebSocket clients receive consistent viewport dimensions across all message types (status and frame messages), matching the actual browser viewport settings. Fixes #950
This commit is contained in:
@@ -339,7 +339,8 @@ impl DaemonState {
|
||||
// Broadcast connection status change to WebSocket clients
|
||||
let connected = self.browser.is_some();
|
||||
let sc = server.is_screencasting().await;
|
||||
server.broadcast_status(connected, sc, 1280, 720);
|
||||
let (vw, vh) = server.viewport().await;
|
||||
server.broadcast_status(connected, sc, vw, vh);
|
||||
// Notify the background CDP event loop that the client changed
|
||||
server.notify_client_changed();
|
||||
}
|
||||
@@ -2992,6 +2993,12 @@ async fn handle_viewport(cmd: &Value, state: &DaemonState) -> Result<Value, Stri
|
||||
let mobile = cmd.get("mobile").and_then(|v| v.as_bool()).unwrap_or(false);
|
||||
|
||||
mgr.set_viewport(width, height, scale, mobile).await?;
|
||||
|
||||
// Update stream server viewport so status messages and screencast use the new dimensions
|
||||
if let Some(ref server) = state.stream_server {
|
||||
server.set_viewport(width as u32, height as u32).await;
|
||||
}
|
||||
|
||||
Ok(json!({ "width": width, "height": height, "deviceScaleFactor": scale, "mobile": mobile }))
|
||||
}
|
||||
|
||||
@@ -3831,6 +3838,11 @@ async fn handle_device(cmd: &Value, state: &DaemonState) -> Result<Value, String
|
||||
mgr.set_viewport(width, height, scale, mobile).await?;
|
||||
mgr.set_user_agent(ua).await?;
|
||||
|
||||
// Update stream server viewport so status messages and screencast use the new dimensions
|
||||
if let Some(ref server) = state.stream_server {
|
||||
server.set_viewport(width as u32, height as u32).await;
|
||||
}
|
||||
|
||||
Ok(json!({
|
||||
"device": name,
|
||||
"width": width,
|
||||
@@ -3852,10 +3864,22 @@ async fn handle_screencast_start(cmd: &Value, state: &mut DaemonState) -> Result
|
||||
return Err("Screencast already active".to_string());
|
||||
}
|
||||
|
||||
// Use stored viewport as default for screencast dimensions
|
||||
let (default_w, default_h) = if let Some(ref server) = state.stream_server {
|
||||
server.viewport().await
|
||||
} else {
|
||||
(1280, 720)
|
||||
};
|
||||
let format = cmd.get("format").and_then(|v| v.as_str()).unwrap_or("jpeg");
|
||||
let quality = cmd.get("quality").and_then(|v| v.as_i64()).unwrap_or(80) as i32;
|
||||
let max_width = cmd.get("maxWidth").and_then(|v| v.as_i64()).unwrap_or(1280) as i32;
|
||||
let max_height = cmd.get("maxHeight").and_then(|v| v.as_i64()).unwrap_or(720) as i32;
|
||||
let max_width = cmd
|
||||
.get("maxWidth")
|
||||
.and_then(|v| v.as_i64())
|
||||
.unwrap_or(default_w as i64) as i32;
|
||||
let max_height = cmd
|
||||
.get("maxHeight")
|
||||
.and_then(|v| v.as_i64())
|
||||
.unwrap_or(default_h as i64) as i32;
|
||||
|
||||
stream::start_screencast(
|
||||
&mgr.client,
|
||||
@@ -3887,7 +3911,8 @@ async fn handle_screencast_stop(state: &mut DaemonState) -> Result<Value, String
|
||||
state.screencasting = false;
|
||||
|
||||
if let Some(ref server) = state.stream_server {
|
||||
server.broadcast_status(true, false, 0, 0);
|
||||
let (vw, vh) = server.viewport().await;
|
||||
server.broadcast_status(true, false, vw, vh);
|
||||
}
|
||||
|
||||
Ok(json!({ "stopped": true }))
|
||||
@@ -4923,6 +4948,11 @@ async fn handle_window_new(cmd: &Value, state: &mut DaemonState) -> Result<Value
|
||||
.and_then(|v| v.as_i64())
|
||||
.unwrap_or(720) as i32;
|
||||
mgr.set_viewport(width, height, 1.0, false).await?;
|
||||
|
||||
// Update stream server viewport
|
||||
if let Some(ref server) = state.stream_server {
|
||||
server.set_viewport(width as u32, height as u32).await;
|
||||
}
|
||||
}
|
||||
|
||||
let total = mgr.page_count();
|
||||
|
||||
@@ -44,6 +44,8 @@ pub struct StreamServer {
|
||||
cdp_session_id: Arc<RwLock<Option<String>>>,
|
||||
client_notify: Arc<Notify>,
|
||||
screencasting: Arc<Mutex<bool>>,
|
||||
viewport_width: Arc<Mutex<u32>>,
|
||||
viewport_height: Arc<Mutex<u32>>,
|
||||
}
|
||||
|
||||
impl StreamServer {
|
||||
@@ -84,6 +86,19 @@ impl StreamServer {
|
||||
*self.screencasting.lock().await
|
||||
}
|
||||
|
||||
/// Update the stored viewport dimensions used by status messages and screencast.
|
||||
pub async fn set_viewport(&self, width: u32, height: u32) {
|
||||
*self.viewport_width.lock().await = width;
|
||||
*self.viewport_height.lock().await = height;
|
||||
}
|
||||
|
||||
/// Get the current viewport dimensions.
|
||||
pub async fn viewport(&self) -> (u32, u32) {
|
||||
let w = *self.viewport_width.lock().await;
|
||||
let h = *self.viewport_height.lock().await;
|
||||
(w, h)
|
||||
}
|
||||
|
||||
async fn start_inner(
|
||||
preferred_port: u16,
|
||||
client_slot: Arc<RwLock<Option<Arc<CdpClient>>>>,
|
||||
@@ -104,6 +119,8 @@ impl StreamServer {
|
||||
let client_notify = Arc::new(Notify::new());
|
||||
let screencasting = Arc::new(Mutex::new(false));
|
||||
let cdp_session_id = Arc::new(RwLock::new(None::<String>));
|
||||
let viewport_width = Arc::new(Mutex::new(1280u32));
|
||||
let viewport_height = Arc::new(Mutex::new(720u32));
|
||||
|
||||
let frame_tx_clone = frame_tx.clone();
|
||||
let client_count_clone = client_count.clone();
|
||||
@@ -113,6 +130,8 @@ impl StreamServer {
|
||||
let cdp_session_clone = cdp_session_id.clone();
|
||||
|
||||
// WebSocket accept loop
|
||||
let vw_clone = viewport_width.clone();
|
||||
let vh_clone = viewport_height.clone();
|
||||
tokio::spawn(async move {
|
||||
accept_loop(
|
||||
listener,
|
||||
@@ -122,6 +141,8 @@ impl StreamServer {
|
||||
notify_clone,
|
||||
screencasting_clone,
|
||||
cdp_session_clone,
|
||||
vw_clone,
|
||||
vh_clone,
|
||||
)
|
||||
.await;
|
||||
});
|
||||
@@ -133,6 +154,8 @@ impl StreamServer {
|
||||
let screencasting_bg = screencasting.clone();
|
||||
let client_count_bg = client_count.clone();
|
||||
let cdp_session_bg = cdp_session_id.clone();
|
||||
let vw_bg = viewport_width.clone();
|
||||
let vh_bg = viewport_height.clone();
|
||||
tokio::spawn(async move {
|
||||
cdp_event_loop(
|
||||
frame_tx_bg,
|
||||
@@ -141,6 +164,8 @@ impl StreamServer {
|
||||
screencasting_bg,
|
||||
client_count_bg,
|
||||
cdp_session_bg,
|
||||
vw_bg,
|
||||
vh_bg,
|
||||
)
|
||||
.await;
|
||||
});
|
||||
@@ -154,6 +179,8 @@ impl StreamServer {
|
||||
cdp_session_id,
|
||||
client_notify,
|
||||
screencasting,
|
||||
viewport_width,
|
||||
viewport_height,
|
||||
},
|
||||
client_slot,
|
||||
))
|
||||
@@ -223,6 +250,8 @@ async fn accept_loop(
|
||||
client_notify: Arc<Notify>,
|
||||
screencasting: Arc<Mutex<bool>>,
|
||||
cdp_session_id: Arc<RwLock<Option<String>>>,
|
||||
viewport_width: Arc<Mutex<u32>>,
|
||||
viewport_height: Arc<Mutex<u32>>,
|
||||
) {
|
||||
while let Ok((stream, addr)) = listener.accept().await {
|
||||
let frame_rx = frame_tx.subscribe();
|
||||
@@ -231,6 +260,8 @@ async fn accept_loop(
|
||||
let client_notify = client_notify.clone();
|
||||
let screencasting = screencasting.clone();
|
||||
let cdp_session_id = cdp_session_id.clone();
|
||||
let vw = viewport_width.clone();
|
||||
let vh = viewport_height.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
handle_ws_client(
|
||||
@@ -242,6 +273,8 @@ async fn accept_loop(
|
||||
client_notify,
|
||||
screencasting,
|
||||
cdp_session_id,
|
||||
vw,
|
||||
vh,
|
||||
)
|
||||
.await;
|
||||
});
|
||||
@@ -258,6 +291,8 @@ async fn handle_ws_client(
|
||||
client_notify: Arc<Notify>,
|
||||
screencasting: Arc<Mutex<bool>>,
|
||||
cdp_session_id: Arc<RwLock<Option<String>>>,
|
||||
viewport_width: Arc<Mutex<u32>>,
|
||||
viewport_height: Arc<Mutex<u32>>,
|
||||
) {
|
||||
let callback =
|
||||
|req: &tokio_tungstenite::tungstenite::handshake::server::Request,
|
||||
@@ -290,17 +325,19 @@ async fn handle_ws_client(
|
||||
|
||||
let (mut ws_tx, mut ws_rx) = ws_stream.split();
|
||||
|
||||
// Send initial status (screencasting:false initially, matching 0.19.0)
|
||||
// Send initial status with current viewport dimensions
|
||||
{
|
||||
let guard = client_slot.read().await;
|
||||
let connected = guard.is_some();
|
||||
let sc = *screencasting.lock().await;
|
||||
let vw = *viewport_width.lock().await;
|
||||
let vh = *viewport_height.lock().await;
|
||||
let status = json!({
|
||||
"type": "status",
|
||||
"connected": connected,
|
||||
"screencasting": sc,
|
||||
"viewportWidth": 1280,
|
||||
"viewportHeight": 720,
|
||||
"viewportWidth": vw,
|
||||
"viewportHeight": vh,
|
||||
});
|
||||
let _ = ws_tx.send(Message::Text(status.to_string())).await;
|
||||
}
|
||||
@@ -351,6 +388,7 @@ async fn handle_ws_client(
|
||||
|
||||
/// Background task that subscribes to CDP events and broadcasts screencast frames in real-time.
|
||||
/// Also handles auto-start/stop of screencast based on WebSocket client count.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn cdp_event_loop(
|
||||
frame_tx: broadcast::Sender<String>,
|
||||
client_slot: Arc<RwLock<Option<Arc<CdpClient>>>>,
|
||||
@@ -358,6 +396,8 @@ async fn cdp_event_loop(
|
||||
screencasting: Arc<Mutex<bool>>,
|
||||
client_count: Arc<Mutex<usize>>,
|
||||
cdp_session_id: Arc<RwLock<Option<String>>>,
|
||||
viewport_width: Arc<Mutex<u32>>,
|
||||
viewport_height: Arc<Mutex<u32>>,
|
||||
) {
|
||||
loop {
|
||||
// Wait until we're notified of a client/connection change
|
||||
@@ -377,14 +417,18 @@ async fn cdp_event_loop(
|
||||
// Get the CDP page session ID for targeted commands
|
||||
let session_id = cdp_session_id.read().await.clone();
|
||||
|
||||
// Use the current viewport dimensions for screencast
|
||||
let vw = *viewport_width.lock().await;
|
||||
let vh = *viewport_height.lock().await;
|
||||
|
||||
let _ = client_arc
|
||||
.send_command(
|
||||
"Page.startScreencast",
|
||||
Some(json!({
|
||||
"format": "jpeg",
|
||||
"quality": 80,
|
||||
"maxWidth": 1280,
|
||||
"maxHeight": 720,
|
||||
"maxWidth": vw,
|
||||
"maxHeight": vh,
|
||||
"everyNthFrame": 1,
|
||||
})),
|
||||
session_id.as_deref(),
|
||||
@@ -396,13 +440,13 @@ async fn cdp_event_loop(
|
||||
*sc = true;
|
||||
}
|
||||
|
||||
// Broadcast screencasting:true status (matching 0.19.0 two-status sequence)
|
||||
// Broadcast screencasting:true status with current viewport
|
||||
let status = json!({
|
||||
"type": "status",
|
||||
"connected": true,
|
||||
"screencasting": true,
|
||||
"viewportWidth": 1280,
|
||||
"viewportHeight": 720,
|
||||
"viewportWidth": vw,
|
||||
"viewportHeight": vh,
|
||||
});
|
||||
let _ = frame_tx.send(status.to_string());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user