From ccd978c9843555bb9d1efefdc1a300cfe7220657 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Tue, 7 Apr 2026 05:39:06 +0200 Subject: [PATCH] fix(cef): ensure physical coordinate for setSize/setPos/setBounds operations on Windows (#15205) --- crates/tauri-runtime-cef/src/cef_impl.rs | 197 ++++++++++------------- crates/tauri-runtime-cef/src/utils.rs | 13 +- 2 files changed, 93 insertions(+), 117 deletions(-) diff --git a/crates/tauri-runtime-cef/src/cef_impl.rs b/crates/tauri-runtime-cef/src/cef_impl.rs index dd80d9573..f208ecf3b 100644 --- a/crates/tauri-runtime-cef/src/cef_impl.rs +++ b/crates/tauri-runtime-cef/src/cef_impl.rs @@ -18,7 +18,8 @@ use std::{ use tauri_runtime::{ ExitRequestedEventAction, RunEvent, UserEvent, dpi::{ - LogicalPosition, LogicalSize, PhysicalPosition, PhysicalRect, PhysicalSize, Position, Size, + LogicalPosition, LogicalSize, PhysicalPosition, PhysicalRect, PhysicalSize, Position, Rect, + Size, }, webview::{InitializationScript, PendingWebview, UriSchemeProtocolHandler, WebviewAttributes}, window::{PendingWindow, WindowEvent, WindowId}, @@ -56,6 +57,45 @@ fn color_to_cef_argb(color: tauri_utils::config::Color) -> u32 { ((a as u32) << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32) } +/// Convert position to the coordinate space expected by CEF. +/// On Windows, CEF uses physical coordinates; on other platforms, logical. +#[inline] +fn position_to_cef(position: Position, scale_factor: f64) -> cef::Point { + #[cfg(windows)] + let p = position.to_physical::(scale_factor); + #[cfg(not(windows))] + let p = position.to_logical::(scale_factor); + cef::Point { x: p.x, y: p.y } +} + +/// Convert size to the coordinate space expected by CEF. +/// On Windows, CEF uses physical coordinates; on other platforms, logical. +#[inline] +fn size_to_cef(size: Size, scale_factor: f64) -> cef::Size { + #[cfg(windows)] + let s = size.to_physical::(scale_factor); + #[cfg(not(windows))] + let s = size.to_logical::(scale_factor); + cef::Size { + width: s.width, + height: s.height, + } +} + +/// Convert rect to the coordinate space expected by CEF. +/// On Windows, CEF uses physical coordinates; on other platforms, logical. +#[inline] +fn rect_to_cef(rect: Rect, scale_factor: f64) -> cef::Rect { + let p = position_to_cef(rect.position, scale_factor); + let s = size_to_cef(rect.size, scale_factor); + cef::Rect { + x: p.x, + y: p.y, + width: s.width, + height: s.height, + } +} + #[inline] fn theme_to_color_variant(theme: Option) -> ColorVariant { match theme { @@ -1367,31 +1407,21 @@ wrap_window_delegate! { && let Some(display) = window.display() { let scale = display.device_scale_factor() as f64; + let size = size_to_cef(inner_size, scale); // On Windows, the size set via CEF APIs is the outer size (including borders), // so we need to adjust it to set the correct inner size. #[cfg(windows)] - let inner_size: tauri_runtime::dpi::Size = { - let size = inner_size.to_physical::(scale); - crate::utils::windows::adjust_size(window.window_handle(), size).into() - }; + let size = crate::utils::windows::adjust_size(window.window_handle(), size); - let logical_size = inner_size.to_logical::(scale); - - window.set_size(Some(&cef::Size { - width: logical_size.width as i32, - height: logical_size.height as i32, - })); + window.set_size(Some(&size)); } if let Some(position) = &a.position && let Some(display) = window.display() { let device_scale_factor = display.device_scale_factor() as f64; - let logical_position = position.to_logical::(device_scale_factor); - window.set_position(Some(&cef::Point { - x: logical_position.x, - y: logical_position.y, - })); + let position = position_to_cef(*position, device_scale_factor); + window.set_position(Some(&position)); } if a.center { @@ -1808,7 +1838,9 @@ fn handle_webview_message( .and_then(|window| window.display()) .map(|d| d.device_scale_factor() as f64) .unwrap_or(1.0); - let logical_position = position.to_logical::(device_scale_factor); + + let position = position_to_cef(position, device_scale_factor); + app_window .webviews .iter() @@ -1816,8 +1848,8 @@ fn handle_webview_message( .map(|wrapper| { let current_bounds = wrapper.inner.bounds(); let new_bounds = cef::Rect { - x: logical_position.x, - y: logical_position.y, + x: position.x, + y: position.y, width: current_bounds.width, height: current_bounds.height, }; @@ -1829,28 +1861,19 @@ fn handle_webview_message( } else { None }; - ( - inner, - new_bounds, - is_browser, - bounds_arc, - logical_position, - window_bounds, - ) + (inner, new_bounds, is_browser, bounds_arc, window_bounds) }) }); - if let Some((inner, new_bounds, is_browser, bounds_arc, logical_position, window_bounds)) = - data - { + if let Some((inner, new_bounds, is_browser, bounds_arc, window_bounds)) = data { inner.set_bounds(Some(&new_bounds)); if is_browser && let Some(b) = &mut *bounds_arc.lock().unwrap() && let Some(wb) = window_bounds { let window_size = LogicalSize::new(wb.width as u32, wb.height as u32); - b.x_rate = logical_position.x as f32 / window_size.width as f32; - b.y_rate = logical_position.y as f32 / window_size.height as f32; + b.x_rate = new_bounds.x as f32 / window_size.width as f32; + b.y_rate = new_bounds.y as f32 / window_size.height as f32; } } } @@ -1865,7 +1888,9 @@ fn handle_webview_message( .and_then(|window| window.display()) .map(|d| d.device_scale_factor() as f64) .unwrap_or(1.0); - let logical_size = size.to_logical::(device_scale_factor); + + let size = size_to_cef(size, device_scale_factor); + app_window .webviews .iter() @@ -1875,8 +1900,8 @@ fn handle_webview_message( let new_bounds = cef::Rect { x: current_bounds.x, y: current_bounds.y, - width: logical_size.width as i32, - height: logical_size.height as i32, + width: size.width, + height: size.height, }; let inner = wrapper.inner.clone(); let bounds_arc = wrapper.bounds.clone(); @@ -1886,26 +1911,19 @@ fn handle_webview_message( } else { None }; - ( - inner, - new_bounds, - is_browser, - bounds_arc, - logical_size, - window_bounds, - ) + (inner, new_bounds, is_browser, bounds_arc, window_bounds) }) }); - if let Some((inner, new_bounds, is_browser, bounds_arc, logical_size, window_bounds)) = data { + if let Some((inner, new_bounds, is_browser, bounds_arc, window_bounds)) = data { inner.set_bounds(Some(&new_bounds)); if is_browser && let Some(b) = &mut *bounds_arc.lock().unwrap() && let Some(wb) = window_bounds { let window_size = LogicalSize::new(wb.width as u32, wb.height as u32); - b.width_rate = logical_size.width as f32 / window_size.width as f32; - b.height_rate = logical_size.height as f32 / window_size.height as f32; + b.width_rate = new_bounds.width as f32 / window_size.width as f32; + b.height_rate = new_bounds.height as f32 / window_size.height as f32; } } } @@ -1920,19 +1938,13 @@ fn handle_webview_message( .and_then(|window| window.display()) .map(|d| d.device_scale_factor() as f64) .unwrap_or(1.0); - let logical_position = bounds.position.to_logical::(device_scale_factor); - let logical_size = bounds.size.to_logical::(device_scale_factor); + + let new_bounds = rect_to_cef(bounds, device_scale_factor); app_window .webviews .iter() .find(|w| w.webview_id == webview_id) .map(|wrapper| { - let new_bounds = cef::Rect { - x: logical_position.x, - y: logical_position.y, - width: logical_size.width as i32, - height: logical_size.height as i32, - }; let inner = wrapper.inner.clone(); let bounds_arc = wrapper.bounds.clone(); let is_browser = wrapper.inner.is_browser(); @@ -1941,38 +1953,21 @@ fn handle_webview_message( } else { None }; - ( - inner, - new_bounds, - is_browser, - bounds_arc, - logical_position, - logical_size, - window_bounds, - ) + (inner, new_bounds, is_browser, bounds_arc, window_bounds) }) }); - if let Some(( - inner, - new_bounds, - is_browser, - bounds_arc, - logical_position, - logical_size, - window_bounds, - )) = data - { + if let Some((inner, new_bounds, is_browser, bounds_arc, window_bounds)) = data { inner.set_bounds(Some(&new_bounds)); if is_browser && let Some(b) = &mut *bounds_arc.lock().unwrap() && let Some(wb) = window_bounds { let window_size = LogicalSize::new(wb.width as u32, wb.height as u32); - b.x_rate = logical_position.x as f32 / window_size.width as f32; - b.y_rate = logical_position.y as f32 / window_size.height as f32; - b.width_rate = logical_size.width as f32 / window_size.width as f32; - b.height_rate = logical_size.height as f32 / window_size.height as f32; + b.x_rate = new_bounds.x as f32 / window_size.width as f32; + b.y_rate = new_bounds.y as f32 / window_size.height as f32; + b.width_rate = new_bounds.width as f32 / window_size.width as f32; + b.height_rate = new_bounds.height as f32 / window_size.height as f32; } } } @@ -2971,19 +2966,14 @@ fn handle_window_message( { let device_scale_factor = display.device_scale_factor() as f64; + let size = size_to_cef(size, device_scale_factor); + // On Windows, the size set via CEF APIs is the outer size (including borders), // so we need to adjust it to set the correct inner size. #[cfg(windows)] - { - let inner_size = size.to_physical::(device_scale_factor); - size = crate::utils::windows::adjust_size(window.window_handle(), inner_size).into(); - } + let size = crate::utils::windows::adjust_size(window.window_handle(), size); - let logical_size = size.to_logical::(device_scale_factor); - window.set_size(Some(&cef::Size { - width: logical_size.width as i32, - height: logical_size.height as i32, - })); + window.set_size(Some(&size)); } } WindowMessage::SetMinSize(size) => { @@ -3007,11 +2997,8 @@ fn handle_window_message( && let Some(display) = window.display() { let device_scale_factor = display.device_scale_factor() as f64; - let logical_position = position.to_logical::(device_scale_factor); - window.set_position(Some(&cef::Point { - x: logical_position.x, - y: logical_position.y, - })); + let position = position_to_cef(position, device_scale_factor); + window.set_position(Some(&position)); } } WindowMessage::SetFullscreen(fullscreen) => { @@ -3303,12 +3290,12 @@ fn create_browser_window( .map(|d| d.device_scale_factor() as f64) .unwrap_or(1.); if let Some(size) = attributes.borrow().inner_size { - let size = size.to_logical::(device_scale_factor); + let size = size_to_cef(size, device_scale_factor); bounds.width = size.width; bounds.height = size.height; } if let Some(position) = attributes.borrow().position { - let position = position.to_logical::(device_scale_factor); + let position = position_to_cef(position, device_scale_factor); bounds.x = position.x; bounds.y = position.y; } @@ -3729,23 +3716,7 @@ pub(crate) fn create_webview( .map(|d| d.device_scale_factor() as f64) .unwrap_or(1.0); - // On Windows, CEF expects physical coordinates for child windows. - #[cfg(windows)] - let logical_position = bounds.position.to_physical::(device_scale_factor); - #[cfg(windows)] - let logical_size = bounds.size.to_physical::(device_scale_factor); - - #[cfg(not(windows))] - let logical_position = bounds.position.to_logical::(device_scale_factor); - #[cfg(not(windows))] - let logical_size = bounds.size.to_logical::(device_scale_factor); - - cef::Rect { - x: logical_position.x, - y: logical_position.y, - width: logical_size.width as i32, - height: logical_size.height as i32, - } + rect_to_cef(bounds, device_scale_factor) }); let window_handle = window.window_handle(); @@ -3771,8 +3742,10 @@ pub(crate) fn create_webview( #[cfg(target_os = "macos")] let window_handle = ensure_valid_content_view(window_handle); - let mut window_info = cef::WindowInfo::default() - .set_as_child(window_handle, bounds.as_ref().unwrap_or(&Rect::default())); + let mut window_info = cef::WindowInfo::default().set_as_child( + window_handle, + bounds.as_ref().unwrap_or(&cef::Rect::default()), + ); window_info.runtime_style = cef_runtime_style; let Some(browser_host) = browser_host_create_browser_sync( diff --git a/crates/tauri-runtime-cef/src/utils.rs b/crates/tauri-runtime-cef/src/utils.rs index 0326c7402..164ecd21c 100644 --- a/crates/tauri-runtime-cef/src/utils.rs +++ b/crates/tauri-runtime-cef/src/utils.rs @@ -15,7 +15,10 @@ pub mod windows { ) } - pub fn adjust_size(hwnd: cef::sys::HWND, size: PhysicalSize) -> PhysicalSize { + /// Adjusts the given size to account for window borders, so that the resulting inner size matches the requested size. + /// + /// Expects and returns a size in physical pixels. + pub fn adjust_size(hwnd: cef::sys::HWND, size: cef::Size) -> cef::Size { let hwnd = HWND(hwnd.0 as _); let mut client_rect = RECT::default(); @@ -31,9 +34,9 @@ pub mod windows { let width_diff = window_width - client_width; let height_diff = window_height - client_height; - PhysicalSize::new( - size.width + width_diff as u32, - size.height + height_diff as u32, - ) + cef::Size { + width: size.width + width_diff, + height: size.height + height_diff, + } } }