From 692213af3ec08770be0886d9ea7b4bb9a49e0f52 Mon Sep 17 00:00:00 2001 From: David Stern Date: Mon, 6 May 2024 18:32:54 -0400 Subject: [PATCH 1/8] Exit instead of panicking on X11 connection loss. (#2) * Exit instead of panicking on X11 connection loss. * formatting * formatting 2, electric boogaloo --- src/platform_impl/linux/x11/mod.rs | 14 +++++++++++++- src/platform_impl/linux/x11/window.rs | 14 +++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index f29b314a7c..fce1f4a26c 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -15,7 +15,7 @@ use calloop::generic::Generic; use calloop::ping::Ping; use calloop::{EventLoop as Loop, Readiness}; use libc::{setlocale, LC_CTYPE}; -use tracing::warn; +use tracing::{error, warn}; use x11rb::connection::RequestConnection; use x11rb::errors::{ConnectError, ConnectionError, IdsExhausted, ReplyError}; @@ -669,6 +669,18 @@ impl ActiveEventLoop { self.xconn .select_xinput_events(self.root, ALL_MASTER_DEVICES, mask) + .map_err(|err| { + // Handle loss of connection to the X server by exiting instead of panicking. + if let X11Error::Connection(_) = err { + error!( + "Detected loss of connection to X server while reading window geometry; \ + exiting application..." + ); + // Use exit code 1 to match the default Xlib I/O error handler. + std::process::exit(1); + } + err + }) .expect_then_ignore_error("Failed to update device event filter"); } diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 7e15d5f242..1acca0b1d0 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -5,7 +5,7 @@ use std::path::Path; use std::sync::{Arc, Mutex, MutexGuard}; use std::{cmp, env}; -use tracing::{debug, info, warn}; +use tracing::{debug, error, info, warn}; use x11rb::connection::Connection; use x11rb::properties::{WmHints, WmSizeHints, WmSizeHintsSpecification}; use x11rb::protocol::shape::{ConnectionExt as ShapeExt, SK, SO}; @@ -1244,6 +1244,18 @@ impl UnownedWindow { // is BadWindow, and if the window handle is bad we have bigger problems. self.xconn .get_geometry(self.xwindow) + .map_err(|err| { + // Handle loss of connection to the X server by exiting instead of panicking. + if let X11Error::Connection(_) = err { + error!( + "Detected loss of connection to X server while reading window geometry; \ + exiting application..." + ); + // Use exit code 1 to match the default Xlib I/O error handler. + std::process::exit(1); + } + err + }) .map(|geo| (geo.width.into(), geo.height.into())) .unwrap() } From de81d1f79a9552999002dbf675ea8b7b044cca75 Mon Sep 17 00:00:00 2001 From: Day Fisher Date: Fri, 17 May 2024 19:15:40 -0400 Subject: [PATCH 2/8] Workaround winit bug where events execute immediately (#4) Winit on web has a bug where the EventLoopProxy will immediately call handlers for a custom event in send_event instead of scheduling it for the next event tick. This causes double-borrows of our MutableAppContext, which hard crashes the app. This is a bandaid fix which pushes those calls on the microtask queue instead. --- src/platform_impl/web/event_loop/proxy.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/platform_impl/web/event_loop/proxy.rs b/src/platform_impl/web/event_loop/proxy.rs index bd8e714635..9bba11bb41 100644 --- a/src/platform_impl/web/event_loop/proxy.rs +++ b/src/platform_impl/web/event_loop/proxy.rs @@ -17,7 +17,13 @@ impl EventLoopProxy { pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { self.sender.send(event).map_err(|SendError(event)| EventLoopClosed(event))?; - self.runner.wake(); + + // Workaround: the we should never be immediately executing new things on the event loop! + let runner = self.runner.clone(); + wasm_bindgen_futures::spawn_local(async move { + runner.wake(); + }); + Ok(()) } } From c5d16891d5231b06f920a23222fd23b541ac9f90 Mon Sep 17 00:00:00 2001 From: Daniel Peng Date: Fri, 24 May 2024 15:40:44 -0700 Subject: [PATCH 3/8] Disable problematic debug assert (#5) --- src/platform_impl/web/web_sys/media_query_handle.rs | 1 + src/platform_impl/web/web_sys/resize_scaling.rs | 13 ++++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/platform_impl/web/web_sys/media_query_handle.rs b/src/platform_impl/web/web_sys/media_query_handle.rs index 766157a681..79c329b258 100644 --- a/src/platform_impl/web/web_sys/media_query_handle.rs +++ b/src/platform_impl/web/web_sys/media_query_handle.rs @@ -30,6 +30,7 @@ impl MediaQueryListHandle { Self { mql, closure } } + #[allow(dead_code)] pub fn mql(&self) -> &MediaQueryList { &self.mql } diff --git a/src/platform_impl/web/web_sys/resize_scaling.rs b/src/platform_impl/web/web_sys/resize_scaling.rs index 4d10b3ac01..1a323d06da 100644 --- a/src/platform_impl/web/web_sys/resize_scaling.rs +++ b/src/platform_impl/web/web_sys/resize_scaling.rs @@ -125,11 +125,14 @@ impl ResizeScaleInternal { (-webkit-device-pixel-ratio: {current_scale})", ); let mql = MediaQueryListHandle::new(window, &media_query, closure); - debug_assert!( - mql.mql().matches(), - "created media query doesn't match, {current_scale} != {}", - super::scale_factor(window) - ); + // TODO(PLAT-806): There's a winit/browser bug that causes this debug_assert + // to trigger when the print dialog is open. To prevent debug builds from + // panicking, we disable the debug_assert for now. + // debug_assert!( + // mql.mql().matches(), + // "created media query doesn't match, {current_scale} != {}", + // super::scale_factor(window) + // ); mql } From fd3e4c2a1083c094377a989282ec8b05d2b8b5cd Mon Sep 17 00:00:00 2001 From: Day Fisher Date: Wed, 12 Jun 2024 17:01:43 -0400 Subject: [PATCH 4/8] Revert "Workaround winit bug where events execute immediately (#4)" This reverts commit 1813f6000d81164f106fa3a29ec9666f56f047a7. This is fixed upstream. --- src/platform_impl/web/event_loop/proxy.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/platform_impl/web/event_loop/proxy.rs b/src/platform_impl/web/event_loop/proxy.rs index 9bba11bb41..bd8e714635 100644 --- a/src/platform_impl/web/event_loop/proxy.rs +++ b/src/platform_impl/web/event_loop/proxy.rs @@ -17,13 +17,7 @@ impl EventLoopProxy { pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { self.sender.send(event).map_err(|SendError(event)| EventLoopClosed(event))?; - - // Workaround: the we should never be immediately executing new things on the event loop! - let runner = self.runner.clone(); - wasm_bindgen_futures::spawn_local(async move { - runner.wake(); - }); - + self.runner.wake(); Ok(()) } } From d2061c8202525a44fe8a54fe169092094c264105 Mon Sep 17 00:00:00 2001 From: Andy Carlson <2yinyang2@gmail.com> Date: Fri, 7 Feb 2025 17:34:36 -0500 Subject: [PATCH 5/8] prevent incorrect shifting of window when dragging onto monitor with different DPI --- src/changelog/unreleased.md | 18 +++++++ src/platform_impl/windows/event_loop.rs | 64 ++----------------------- 2 files changed, 21 insertions(+), 61 deletions(-) diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index f3a0f6d2c3..cb8e6a6727 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -39,3 +39,21 @@ The migration guide could reference other migration examples in the current changelog entry. ## Unreleased + +### Changed + +- On Web, let events wake up event loop immediately when using + `ControlFlow::Poll`. + +### Fixed + +- On Web, fix `EventLoopProxy::send_event()` triggering event loop immediately + when not called from inside the event loop. Now queues a microtask instead. +- On Windows, prevent incorrect shifting when dragging window onto a monitor + with different DPI. + +### Removed + +- Remove `EventLoop::run`. +- Remove `EventLoopExtRunOnDemand::run_on_demand`. +- Remove `EventLoopExtPumpEvents::pump_events`. diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 6d60c1c524..14d4eac054 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -21,8 +21,8 @@ use windows_sys::Win32::Foundation::{ GetLastError, FALSE, HANDLE, HWND, LPARAM, LRESULT, POINT, RECT, WAIT_FAILED, WPARAM, }; use windows_sys::Win32::Graphics::Gdi::{ - GetMonitorInfoW, MonitorFromRect, MonitorFromWindow, RedrawWindow, ScreenToClient, - ValidateRect, MONITORINFO, MONITOR_DEFAULTTONULL, RDW_INTERNALPAINT, SC_SCREENSAVE, + MonitorFromRect, RedrawWindow, ScreenToClient, ValidateRect, MONITOR_DEFAULTTONULL, + RDW_INTERNALPAINT, SC_SCREENSAVE, }; use windows_sys::Win32::System::Ole::RevokeDragDrop; use windows_sys::Win32::System::Threading::{ @@ -2344,65 +2344,7 @@ unsafe fn public_window_callback_inner( conservative_rect.right += bias; } - // Check to see if the new window rect is on the monitor with the new DPI factor. - // If it isn't, offset the window so that it is. - let new_dpi_monitor = unsafe { MonitorFromWindow(window, MONITOR_DEFAULTTONULL) }; - let conservative_rect_monitor = - unsafe { MonitorFromRect(&conservative_rect, MONITOR_DEFAULTTONULL) }; - new_outer_rect = if conservative_rect_monitor == new_dpi_monitor { - conservative_rect - } else { - let get_monitor_rect = |monitor| { - let mut monitor_info = MONITORINFO { - cbSize: mem::size_of::() as _, - ..unsafe { mem::zeroed() } - }; - unsafe { GetMonitorInfoW(monitor, &mut monitor_info) }; - monitor_info.rcMonitor - }; - let wrong_monitor = conservative_rect_monitor; - let wrong_monitor_rect = get_monitor_rect(wrong_monitor); - let new_monitor_rect = get_monitor_rect(new_dpi_monitor); - - // The direction to nudge the window in to get the window onto the monitor with - // the new DPI factor. We calculate this by seeing which monitor edges are - // shared and nudging away from the wrong monitor based on those. - #[allow(clippy::bool_to_int_with_if)] - let delta_nudge_to_dpi_monitor = ( - if wrong_monitor_rect.left == new_monitor_rect.right { - -1 - } else if wrong_monitor_rect.right == new_monitor_rect.left { - 1 - } else { - 0 - }, - if wrong_monitor_rect.bottom == new_monitor_rect.top { - 1 - } else if wrong_monitor_rect.top == new_monitor_rect.bottom { - -1 - } else { - 0 - }, - ); - - let abort_after_iterations = new_monitor_rect.right - new_monitor_rect.left - + new_monitor_rect.bottom - - new_monitor_rect.top; - for _ in 0..abort_after_iterations { - conservative_rect.left += delta_nudge_to_dpi_monitor.0; - conservative_rect.right += delta_nudge_to_dpi_monitor.0; - conservative_rect.top += delta_nudge_to_dpi_monitor.1; - conservative_rect.bottom += delta_nudge_to_dpi_monitor.1; - - if unsafe { MonitorFromRect(&conservative_rect, MONITOR_DEFAULTTONULL) } - == new_dpi_monitor - { - break; - } - } - - conservative_rect - }; + new_outer_rect = conservative_rect; } unsafe { From 0ffd1109118c7d2fb123822a9cda7831dbcfcf4e Mon Sep 17 00:00:00 2001 From: Andy Carlson <2yinyang2@gmail.com> Date: Fri, 14 Feb 2025 10:44:25 -0500 Subject: [PATCH 6/8] Revert "Merge pull request #8 from warpdotdev/andy/fix-windows-dpi-changed-3" This reverts commit e1958d8ff7d2504cd5e99077b3915cac87e4caba, reversing changes made to fd5ff180fe2d321f235b6894ba06aeff38f0d1a8. --- src/changelog/unreleased.md | 2 - src/platform_impl/windows/event_loop.rs | 66 +++++++++++++++++++++++-- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index cb8e6a6727..7c663de60e 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -49,8 +49,6 @@ changelog entry. - On Web, fix `EventLoopProxy::send_event()` triggering event loop immediately when not called from inside the event loop. Now queues a microtask instead. -- On Windows, prevent incorrect shifting when dragging window onto a monitor - with different DPI. ### Removed diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 14d4eac054..0872931cb0 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -21,8 +21,8 @@ use windows_sys::Win32::Foundation::{ GetLastError, FALSE, HANDLE, HWND, LPARAM, LRESULT, POINT, RECT, WAIT_FAILED, WPARAM, }; use windows_sys::Win32::Graphics::Gdi::{ - MonitorFromRect, RedrawWindow, ScreenToClient, ValidateRect, MONITOR_DEFAULTTONULL, - RDW_INTERNALPAINT, SC_SCREENSAVE, + GetMonitorInfoW, MonitorFromRect, MonitorFromWindow, RedrawWindow, ScreenToClient, + ValidateRect, MONITORINFO, MONITOR_DEFAULTTONULL, RDW_INTERNALPAINT, SC_SCREENSAVE, }; use windows_sys::Win32::System::Ole::RevokeDragDrop; use windows_sys::Win32::System::Threading::{ @@ -2344,7 +2344,65 @@ unsafe fn public_window_callback_inner( conservative_rect.right += bias; } - new_outer_rect = conservative_rect; + // Check to see if the new window rect is on the monitor with the new DPI factor. + // If it isn't, offset the window so that it is. + let new_dpi_monitor = unsafe { MonitorFromWindow(window, MONITOR_DEFAULTTONULL) }; + let conservative_rect_monitor = + unsafe { MonitorFromRect(&conservative_rect, MONITOR_DEFAULTTONULL) }; + new_outer_rect = if conservative_rect_monitor == new_dpi_monitor { + conservative_rect + } else { + let get_monitor_rect = |monitor| { + let mut monitor_info = MONITORINFO { + cbSize: mem::size_of::() as _, + ..unsafe { mem::zeroed() } + }; + unsafe { GetMonitorInfoW(monitor, &mut monitor_info) }; + monitor_info.rcMonitor + }; + let wrong_monitor = conservative_rect_monitor; + let wrong_monitor_rect = get_monitor_rect(wrong_monitor); + let new_monitor_rect = get_monitor_rect(new_dpi_monitor); + + // The direction to nudge the window in to get the window onto the monitor with + // the new DPI factor. We calculate this by seeing which monitor edges are + // shared and nudging away from the wrong monitor based on those. + #[allow(clippy::bool_to_int_with_if)] + let delta_nudge_to_dpi_monitor = ( + if wrong_monitor_rect.left == new_monitor_rect.right { + -1 + } else if wrong_monitor_rect.right == new_monitor_rect.left { + 1 + } else { + 0 + }, + if wrong_monitor_rect.bottom == new_monitor_rect.top { + 1 + } else if wrong_monitor_rect.top == new_monitor_rect.bottom { + -1 + } else { + 0 + }, + ); + + let abort_after_iterations = new_monitor_rect.right - new_monitor_rect.left + + new_monitor_rect.bottom + - new_monitor_rect.top; + for _ in 0..abort_after_iterations { + conservative_rect.left += delta_nudge_to_dpi_monitor.0; + conservative_rect.right += delta_nudge_to_dpi_monitor.0; + conservative_rect.top += delta_nudge_to_dpi_monitor.1; + conservative_rect.bottom += delta_nudge_to_dpi_monitor.1; + + if unsafe { MonitorFromRect(&conservative_rect, MONITOR_DEFAULTTONULL) } + == new_dpi_monitor + { + break; + } + } + + conservative_rect + }; } unsafe { @@ -2493,7 +2551,7 @@ unsafe extern "system" fn thread_event_target_callback( if userdata_removed { drop(userdata); } else { - Box::leak(userdata); + Box::into_raw(userdata); } result } From fea4998d26418ec627f1ced2779119149ffec9c3 Mon Sep 17 00:00:00 2001 From: Andy Carlson <2yinyang2@gmail.com> Date: Fri, 14 Feb 2025 11:19:31 -0500 Subject: [PATCH 7/8] bring the Tauri version of the DPI-change fix downstream --- Cargo.toml | 1 + src/changelog/unreleased.md | 2 + src/platform_impl/windows/event_loop.rs | 191 ++++++++++++------------ src/platform_impl/windows/util.rs | 4 + 4 files changed, 106 insertions(+), 92 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 71f7c58113..5b2d09a29f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -220,6 +220,7 @@ features = [ [target.'cfg(target_os = "windows")'.dependencies] unicode-segmentation = "1.7.1" +windows-version = "0.1.1" [target.'cfg(target_os = "windows")'.dependencies.windows-sys] version = "0.52.0" diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index 7c663de60e..cb8e6a6727 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -49,6 +49,8 @@ changelog entry. - On Web, fix `EventLoopProxy::send_event()` triggering event loop immediately when not called from inside the event loop. Now queues a microtask instead. +- On Windows, prevent incorrect shifting when dragging window onto a monitor + with different DPI. ### Removed diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 0872931cb0..a5597e8f3d 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -2306,103 +2306,110 @@ unsafe fn public_window_callback_inner( } let new_outer_rect: RECT; - { - let suggested_ul = - (suggested_rect.left + margin_left, suggested_rect.top + margin_top); - - let mut conservative_rect = RECT { - left: suggested_ul.0, - top: suggested_ul.1, - right: suggested_ul.0 + new_physical_inner_size.width as i32, - bottom: suggested_ul.1 + new_physical_inner_size.height as i32, - }; - - conservative_rect = window_flags - .adjust_rect(window, conservative_rect) - .unwrap_or(conservative_rect); - - // If we're dragging the window, offset the window so that the cursor's - // relative horizontal position in the title bar is preserved. - if dragging_window { - let bias = { - let cursor_pos = { - let mut pos = unsafe { mem::zeroed() }; - unsafe { GetCursorPos(&mut pos) }; - pos - }; - let suggested_cursor_horizontal_ratio = (cursor_pos.x - suggested_rect.left) - as f64 - / (suggested_rect.right - suggested_rect.left) as f64; - - (cursor_pos.x - - (suggested_cursor_horizontal_ratio - * (conservative_rect.right - conservative_rect.left) as f64) - as i32) - - conservative_rect.left + if util::WIN_VERSION.build < 22000 { + // The window position needs adjustment on Windows 10. + { + let suggested_ul = + (suggested_rect.left + margin_left, suggested_rect.top + margin_top); + + let mut conservative_rect = RECT { + left: suggested_ul.0, + top: suggested_ul.1, + right: suggested_ul.0 + new_physical_inner_size.width as i32, + bottom: suggested_ul.1 + new_physical_inner_size.height as i32, }; - conservative_rect.left += bias; - conservative_rect.right += bias; - } - // Check to see if the new window rect is on the monitor with the new DPI factor. - // If it isn't, offset the window so that it is. - let new_dpi_monitor = unsafe { MonitorFromWindow(window, MONITOR_DEFAULTTONULL) }; - let conservative_rect_monitor = - unsafe { MonitorFromRect(&conservative_rect, MONITOR_DEFAULTTONULL) }; - new_outer_rect = if conservative_rect_monitor == new_dpi_monitor { - conservative_rect - } else { - let get_monitor_rect = |monitor| { - let mut monitor_info = MONITORINFO { - cbSize: mem::size_of::() as _, - ..unsafe { mem::zeroed() } + conservative_rect = window_flags + .adjust_rect(window, conservative_rect) + .unwrap_or(conservative_rect); + + // If we're dragging the window, offset the window so that the cursor's + // relative horizontal position in the title bar is preserved. + if dragging_window { + let bias = { + let cursor_pos = { + let mut pos = unsafe { mem::zeroed() }; + unsafe { GetCursorPos(&mut pos) }; + pos + }; + let suggested_cursor_horizontal_ratio = + (cursor_pos.x - suggested_rect.left) as f64 + / (suggested_rect.right - suggested_rect.left) as f64; + + (cursor_pos.x + - (suggested_cursor_horizontal_ratio + * (conservative_rect.right - conservative_rect.left) as f64) + as i32) + - conservative_rect.left }; - unsafe { GetMonitorInfoW(monitor, &mut monitor_info) }; - monitor_info.rcMonitor - }; - let wrong_monitor = conservative_rect_monitor; - let wrong_monitor_rect = get_monitor_rect(wrong_monitor); - let new_monitor_rect = get_monitor_rect(new_dpi_monitor); - - // The direction to nudge the window in to get the window onto the monitor with - // the new DPI factor. We calculate this by seeing which monitor edges are - // shared and nudging away from the wrong monitor based on those. - #[allow(clippy::bool_to_int_with_if)] - let delta_nudge_to_dpi_monitor = ( - if wrong_monitor_rect.left == new_monitor_rect.right { - -1 - } else if wrong_monitor_rect.right == new_monitor_rect.left { - 1 - } else { - 0 - }, - if wrong_monitor_rect.bottom == new_monitor_rect.top { - 1 - } else if wrong_monitor_rect.top == new_monitor_rect.bottom { - -1 - } else { - 0 - }, - ); + conservative_rect.left += bias; + conservative_rect.right += bias; + } - let abort_after_iterations = new_monitor_rect.right - new_monitor_rect.left - + new_monitor_rect.bottom - - new_monitor_rect.top; - for _ in 0..abort_after_iterations { - conservative_rect.left += delta_nudge_to_dpi_monitor.0; - conservative_rect.right += delta_nudge_to_dpi_monitor.0; - conservative_rect.top += delta_nudge_to_dpi_monitor.1; - conservative_rect.bottom += delta_nudge_to_dpi_monitor.1; - - if unsafe { MonitorFromRect(&conservative_rect, MONITOR_DEFAULTTONULL) } - == new_dpi_monitor - { - break; + // Check to see if the new window rect is on the monitor with the new DPI factor. + // If it isn't, offset the window so that it is. + let new_dpi_monitor = + unsafe { MonitorFromWindow(window, MONITOR_DEFAULTTONULL) }; + let conservative_rect_monitor = + unsafe { MonitorFromRect(&conservative_rect, MONITOR_DEFAULTTONULL) }; + new_outer_rect = if conservative_rect_monitor == new_dpi_monitor { + conservative_rect + } else { + let get_monitor_rect = |monitor| { + let mut monitor_info = MONITORINFO { + cbSize: mem::size_of::() as _, + ..unsafe { mem::zeroed() } + }; + unsafe { GetMonitorInfoW(monitor, &mut monitor_info) }; + monitor_info.rcMonitor + }; + let wrong_monitor = conservative_rect_monitor; + let wrong_monitor_rect = get_monitor_rect(wrong_monitor); + let new_monitor_rect = get_monitor_rect(new_dpi_monitor); + + // The direction to nudge the window in to get the window onto the monitor with + // the new DPI factor. We calculate this by seeing which monitor edges are + // shared and nudging away from the wrong monitor based on those. + #[allow(clippy::bool_to_int_with_if)] + let delta_nudge_to_dpi_monitor = ( + if wrong_monitor_rect.left == new_monitor_rect.right { + -1 + } else if wrong_monitor_rect.right == new_monitor_rect.left { + 1 + } else { + 0 + }, + if wrong_monitor_rect.bottom == new_monitor_rect.top { + 1 + } else if wrong_monitor_rect.top == new_monitor_rect.bottom { + -1 + } else { + 0 + }, + ); + + let abort_after_iterations = new_monitor_rect.right - new_monitor_rect.left + + new_monitor_rect.bottom + - new_monitor_rect.top; + for _ in 0..abort_after_iterations { + conservative_rect.left += delta_nudge_to_dpi_monitor.0; + conservative_rect.right += delta_nudge_to_dpi_monitor.0; + conservative_rect.top += delta_nudge_to_dpi_monitor.1; + conservative_rect.bottom += delta_nudge_to_dpi_monitor.1; + + if unsafe { MonitorFromRect(&conservative_rect, MONITOR_DEFAULTTONULL) } + == new_dpi_monitor + { + break; + } } - } - conservative_rect - }; + conservative_rect + }; + } + } else { + // The suggested position is fine w/o adjustment on Windows 11. + new_outer_rect = suggested_rect } unsafe { @@ -2551,7 +2558,7 @@ unsafe extern "system" fn thread_event_target_callback( if userdata_removed { drop(userdata); } else { - Box::into_raw(userdata); + Box::leak(userdata); } result } diff --git a/src/platform_impl/windows/util.rs b/src/platform_impl/windows/util.rs index a3a7705d2b..3bbd79b8e9 100644 --- a/src/platform_impl/windows/util.rs +++ b/src/platform_impl/windows/util.rs @@ -3,6 +3,7 @@ use std::iter::once; use std::ops::BitAnd; use std::os::windows::prelude::{OsStrExt, OsStringExt}; use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::LazyLock; use std::{io, mem, ptr}; use crate::utils::Lazy; @@ -26,6 +27,9 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{ use crate::window::CursorIcon; +pub static WIN_VERSION: LazyLock = + LazyLock::new(windows_version::OsVersion::current); + pub fn encode_wide(string: impl AsRef) -> Vec { string.as_ref().encode_wide().chain(once(0)).collect() } From 60bf3e865fb98b3594fff09be03c43a15c4590d6 Mon Sep 17 00:00:00 2001 From: David Stern Date: Sat, 7 Feb 2026 12:50:40 -0500 Subject: [PATCH 8/8] Fix double-borrow crashes on resize. (#11) --- .../web/event_loop/window_target.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index d741f09872..79703aff88 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -553,10 +553,19 @@ impl ActiveEventLoop { let canvas = canvas_clone.clone(); move |new_size| { - let canvas = canvas.borrow(); - canvas.set_current_size(new_size); - if canvas.old_size() != new_size { - canvas.set_old_size(new_size); + // Release the canvas borrow before sending events, because event + // processing can synchronously re-enter and attempt to borrow_mut + // the canvas (e.g. to update the cursor), which would panic. + let size_changed = { + let canvas = canvas.borrow(); + canvas.set_current_size(new_size); + let changed = canvas.old_size() != new_size; + if changed { + canvas.set_old_size(new_size); + } + changed + }; + if size_changed { runner.send_event(Event::WindowEvent { window_id: RootWindowId(id), event: WindowEvent::Resized(new_size),