From 46eb9e5223036d103181c849187770917255eaac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20L=C3=BCder?= Date: Thu, 13 Nov 2025 10:51:13 -0600 Subject: [PATCH] Update scale factor and drawable size when macOS window changes screen (#38269) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary Fixes UI scaling issue that occurs when starting Zed after disconnecting an external monitor on macOS. The window's scale factor and drawable size are now properly updated when the window changes screens. Problem Description When an external monitor is disconnected and Zed is started with only the built-in screen active, the UI scale becomes incorrect. This happens because: 1. macOS triggers the `window_did_change_screen` callback when a window moves between displays (including when displays are disconnected) 2. The existing implementation only restarted the display link but didn't update the window's scale factor or drawable size 3. This left the window with stale scaling information from the previous display configuration Root Cause The `window_did_change_screen` callback in `crates/gpui/src/platform/mac/window.rs` was missing the logic to update the window's scale factor and drawable size when moving between screens. This logic was only present in the `view_did_change_backing_properties callback`, which isn't triggered when external monitors are disconnected. Solution - Extracted common logic: Created a new `update_window_scale_factor()` function that encapsulates the scale factor and drawable size update logic - Added scale factor update to screen change: Modified `window_did_change_screen` to call this function after restarting the display link - Refactored existing code: Updated `view_did_change_backing_properties` to use the new shared function, reducing code duplication The fix ensures that whenever a window changes screens (due to monitor disconnect, reconnect, or manual movement), the scale factor, drawable size, and renderer state are properly synchronized. Testing - ✅ Verified that UI scaling remains correct after disconnecting external monitor - ✅ Confirmed that reconnecting external monitor works properly - ✅ Tested that manual window movement between displays updates scaling correctly - ✅ No regressions observed in normal window operations To verity my fix worked I had to copy my preview workspace over my dev workspace, once I had done this I could reproduce the issue on main consistently. After switching to the branch with this fix the issue was resolved. The fix is similar to what was done on https://github.com/zed-industries/zed/pull/35686 (Windows) Closes #37245 #38229 Release Notes: - Fixed: Update scale factor and drawable size when macOS window changes screen --------- Co-authored-by: Kate --- crates/gpui/src/platform/mac/window.rs | 48 +++++++++++++++----------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 11ea4fb7e272c0660c7981cabf8f8c74ffa71830..53a5688ad6b78cda8f610e4acc810a7df58cf47b 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -1967,10 +1967,36 @@ extern "C" fn window_did_move(this: &Object, _: Sel, _: id) { } } +// Update the window scale factor and drawable size, and call the resize callback if any. +fn update_window_scale_factor(window_state: &Arc>) { + let mut lock = window_state.as_ref().lock(); + let scale_factor = lock.scale_factor(); + let size = lock.content_size(); + let drawable_size = size.to_device_pixels(scale_factor); + unsafe { + let _: () = msg_send![ + lock.renderer.layer(), + setContentsScale: scale_factor as f64 + ]; + } + + lock.renderer.update_drawable_size(drawable_size); + + if let Some(mut callback) = lock.resize_callback.take() { + let content_size = lock.content_size(); + let scale_factor = lock.scale_factor(); + drop(lock); + callback(content_size, scale_factor); + window_state.as_ref().lock().resize_callback = Some(callback); + }; +} + extern "C" fn window_did_change_screen(this: &Object, _: Sel, _: id) { let window_state = unsafe { get_window_state(this) }; let mut lock = window_state.as_ref().lock(); lock.start_display_link(); + drop(lock); + update_window_scale_factor(&window_state); } extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id) { @@ -2079,27 +2105,7 @@ extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id { extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) { let window_state = unsafe { get_window_state(this) }; - let mut lock = window_state.as_ref().lock(); - - let scale_factor = lock.scale_factor(); - let size = lock.content_size(); - let drawable_size = size.to_device_pixels(scale_factor); - unsafe { - let _: () = msg_send![ - lock.renderer.layer(), - setContentsScale: scale_factor as f64 - ]; - } - - lock.renderer.update_drawable_size(drawable_size); - - if let Some(mut callback) = lock.resize_callback.take() { - let content_size = lock.content_size(); - let scale_factor = lock.scale_factor(); - drop(lock); - callback(content_size, scale_factor); - window_state.as_ref().lock().resize_callback = Some(callback); - }; + update_window_scale_factor(&window_state); } extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {