Fix issues where screen and window sizes contained Pixels, but were declared as DevicePixels (#12991)

Max Brunsfeld , Conrad Irwin , and Mikayla created

On most platforms, things were working correctly, but had the wrong
type. On X11, there were some problems with window and display size
calculations.

Release Notes:

- Fixed issues with window positioning on X11

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Co-authored-by: Mikayla <mikayla@zed.dev>

Change summary

crates/assistant/src/prompt_library.rs            |  10 -
crates/collab_ui/src/collab_ui.rs                 |  20 +-
crates/editor/src/editor_tests.rs                 |   4 
crates/gpui/examples/image/image.rs               |   4 
crates/gpui/examples/window_positioning.rs        |  17 +-
crates/gpui/src/geometry.rs                       |  75 +++++++---
crates/gpui/src/platform.rs                       |  18 +-
crates/gpui/src/platform/blade/blade_renderer.rs  |  10 
crates/gpui/src/platform/linux/wayland/client.rs  |   4 
crates/gpui/src/platform/linux/wayland/display.rs |   6 
crates/gpui/src/platform/linux/wayland/window.rs  | 114 +++++++---------
crates/gpui/src/platform/linux/x11/client.rs      |  16 +
crates/gpui/src/platform/linux/x11/display.rs     |  18 +-
crates/gpui/src/platform/linux/x11/window.rs      |  36 +++--
crates/gpui/src/platform/mac/display.rs           |  11 -
crates/gpui/src/platform/mac/metal_renderer.rs    |   8 
crates/gpui/src/platform/mac/window.rs            |  68 +++------
crates/gpui/src/platform/test/display.rs          |  12 -
crates/gpui/src/platform/test/window.rs           |  16 +-
crates/gpui/src/platform/windows/display.rs       |  36 ++--
crates/gpui/src/platform/windows/events.rs        |  23 +-
crates/gpui/src/platform/windows/window.rs        |  59 ++++----
crates/gpui/src/window.rs                         |  13 -
crates/workspace/src/persistence.rs               |  76 ++++-------
crates/workspace/src/workspace.rs                 |  24 +-
25 files changed, 331 insertions(+), 367 deletions(-)

Detailed changes

crates/assistant/src/prompt_library.rs 🔗

@@ -14,8 +14,8 @@ use futures::{
 use fuzzy::StringMatchCandidate;
 use gpui::{
     actions, percentage, point, size, Animation, AnimationExt, AppContext, BackgroundExecutor,
-    Bounds, DevicePixels, EventEmitter, Global, PromptLevel, ReadGlobal, Subscription, Task,
-    TitlebarOptions, Transformation, UpdateGlobal, View, WindowBounds, WindowHandle, WindowOptions,
+    Bounds, EventEmitter, Global, PromptLevel, ReadGlobal, Subscription, Task, TitlebarOptions,
+    Transformation, UpdateGlobal, View, WindowBounds, WindowHandle, WindowOptions,
 };
 use heed::{types::SerdeBincode, Database, RoTxn};
 use language::{language_settings::SoftWrap, Buffer, LanguageRegistry};
@@ -80,11 +80,7 @@ pub fn open_prompt_library(
         cx.spawn(|cx| async move {
             let store = store.await?;
             cx.update(|cx| {
-                let bounds = Bounds::centered(
-                    None,
-                    size(DevicePixels::from(1024), DevicePixels::from(768)),
-                    cx,
-                );
+                let bounds = Bounds::centered(None, size(px(1024.0), px(768.0)), cx);
                 cx.open_window(
                     WindowOptions {
                         titlebar: Some(TitlebarOptions {

crates/collab_ui/src/collab_ui.rs 🔗

@@ -13,8 +13,8 @@ use call::{report_call_event_for_room, ActiveCall};
 pub use collab_panel::CollabPanel;
 pub use collab_titlebar_item::CollabTitlebarItem;
 use gpui::{
-    actions, point, AppContext, DevicePixels, Pixels, PlatformDisplay, Size, Task,
-    WindowBackgroundAppearance, WindowBounds, WindowContext, WindowKind, WindowOptions,
+    actions, point, AppContext, Pixels, PlatformDisplay, Size, Task, WindowBackgroundAppearance,
+    WindowBounds, WindowContext, WindowKind, WindowOptions,
 };
 use panel_settings::MessageEditorSettings;
 pub use panel_settings::{
@@ -22,6 +22,7 @@ pub use panel_settings::{
 };
 use release_channel::ReleaseChannel;
 use settings::Settings;
+use ui::px;
 use workspace::{notifications::DetachAndPromptErr, AppState};
 
 actions!(
@@ -96,22 +97,19 @@ pub fn toggle_deafen(_: &ToggleDeafen, cx: &mut AppContext) {
 
 fn notification_window_options(
     screen: Rc<dyn PlatformDisplay>,
-    window_size: Size<Pixels>,
+    size: Size<Pixels>,
     cx: &AppContext,
 ) -> WindowOptions {
-    let notification_margin_width = DevicePixels::from(16);
-    let notification_margin_height = DevicePixels::from(-0) - DevicePixels::from(48);
+    let notification_margin_width = px(16.);
+    let notification_margin_height = px(-48.);
 
-    let screen_bounds = screen.bounds();
-    let size: Size<DevicePixels> = window_size.into();
-
-    let bounds = gpui::Bounds::<DevicePixels> {
-        origin: screen_bounds.upper_right()
+    let bounds = gpui::Bounds::<Pixels> {
+        origin: screen.bounds().upper_right()
             - point(
                 size.width + notification_margin_width,
                 notification_margin_height,
             ),
-        size: window_size.into(),
+        size,
     };
 
     let app_id = ReleaseChannel::global(cx).app_id();

crates/editor/src/editor_tests.rs 🔗

@@ -7654,8 +7654,8 @@ async fn test_following(cx: &mut gpui::TestAppContext) {
         cx.open_window(
             WindowOptions {
                 window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
-                    gpui::Point::new(0.into(), 0.into()),
-                    gpui::Point::new(10.into(), 80.into()),
+                    gpui::Point::new(px(0.), px(0.)),
+                    gpui::Point::new(px(10.), px(80.)),
                 ))),
                 ..Default::default()
             },

crates/gpui/examples/image/image.rs 🔗

@@ -80,8 +80,8 @@ fn main() {
             }),
 
             window_bounds: Some(WindowBounds::Windowed(Bounds {
-                size: size(px(1100.), px(600.)).into(),
-                origin: Point::new(DevicePixels::from(200), DevicePixels::from(200)),
+                size: size(px(1100.), px(600.)),
+                origin: Point::new(px(200.), px(200.)),
             })),
 
             ..Default::default()

crates/gpui/examples/window_positioning.rs 🔗

@@ -24,21 +24,18 @@ fn main() {
 
         for screen in cx.displays() {
             let options = {
-                let popup_margin_width = DevicePixels::from(16);
-                let popup_margin_height = DevicePixels::from(-0) - DevicePixels::from(48);
+                let margin_right = px(16.);
+                let margin_height = px(-48.);
 
-                let window_size = Size {
+                let size = Size {
                     width: px(400.),
                     height: px(72.),
                 };
 
-                let screen_bounds = screen.bounds();
-                let size: Size<DevicePixels> = window_size.into();
-
-                let bounds = gpui::Bounds::<DevicePixels> {
-                    origin: screen_bounds.upper_right()
-                        - point(size.width + popup_margin_width, popup_margin_height),
-                    size: window_size.into(),
+                let bounds = gpui::Bounds::<Pixels> {
+                    origin: screen.bounds().upper_right()
+                        - point(size.width + margin_right, margin_height),
+                    size,
                 };
 
                 WindowOptions {

crates/gpui/src/geometry.rs 🔗

@@ -363,15 +363,6 @@ pub struct Size<T: Clone + Default + Debug> {
     pub height: T,
 }
 
-impl From<Size<DevicePixels>> for Size<Pixels> {
-    fn from(size: Size<DevicePixels>) -> Self {
-        Size {
-            width: Pixels(size.width.0 as f32),
-            height: Pixels(size.height.0 as f32),
-        }
-    }
-}
-
 /// Constructs a new `Size<T>` with the provided width and height.
 ///
 /// # Arguments
@@ -633,15 +624,6 @@ impl<T: Clone + Default + Debug> From<Point<T>> for Size<T> {
     }
 }
 
-impl From<Size<Pixels>> for Size<DevicePixels> {
-    fn from(size: Size<Pixels>) -> Self {
-        Size {
-            width: DevicePixels(size.width.0 as i32),
-            height: DevicePixels(size.height.0 as i32),
-        }
-    }
-}
-
 impl From<Size<Pixels>> for Size<DefiniteLength> {
     fn from(size: Size<Pixels>) -> Self {
         Size {
@@ -722,28 +704,27 @@ pub struct Bounds<T: Clone + Default + Debug> {
     pub size: Size<T>,
 }
 
-impl Bounds<DevicePixels> {
+impl Bounds<Pixels> {
     /// Generate a centered bounds for the given display or primary display if none is provided
     pub fn centered(
         display_id: Option<DisplayId>,
-        size: impl Into<Size<DevicePixels>>,
+        size: Size<Pixels>,
         cx: &mut AppContext,
     ) -> Self {
         let display = display_id
             .and_then(|id| cx.find_display(id))
             .or_else(|| cx.primary_display());
 
-        let size = size.into();
         display
             .map(|display| {
                 let center = display.bounds().center();
                 Bounds {
-                    origin: point(center.x - size.width / 2, center.y - size.height / 2),
+                    origin: point(center.x - size.width / 2., center.y - size.height / 2.),
                     size,
                 }
             })
             .unwrap_or_else(|| Bounds {
-                origin: point(DevicePixels(0), DevicePixels(0)),
+                origin: point(px(0.), px(0.)),
                 size,
             })
     }
@@ -757,8 +738,8 @@ impl Bounds<DevicePixels> {
         display
             .map(|display| display.bounds())
             .unwrap_or_else(|| Bounds {
-                origin: point(DevicePixels(0), DevicePixels(0)),
-                size: size(DevicePixels(1024), DevicePixels(768)),
+                origin: point(px(0.), px(0.)),
+                size: size(px(1024.), px(768.)),
             })
     }
 }
@@ -1309,6 +1290,26 @@ impl<T: PartialOrd + Default + Debug + Clone> Bounds<T> {
     }
 }
 
+impl Size<DevicePixels> {
+    /// Converts the size from physical to logical pixels.
+    pub(crate) fn to_pixels(self, scale_factor: f32) -> Size<Pixels> {
+        size(
+            px(self.width.0 as f32 / scale_factor),
+            px(self.height.0 as f32 / scale_factor),
+        )
+    }
+}
+
+impl Size<Pixels> {
+    /// Converts the size from physical to logical pixels.
+    pub(crate) fn to_device_pixels(self, scale_factor: f32) -> Size<DevicePixels> {
+        size(
+            DevicePixels((self.width.0 * scale_factor) as i32),
+            DevicePixels((self.height.0 * scale_factor) as i32),
+        )
+    }
+}
+
 impl Bounds<Pixels> {
     /// Scales the bounds by a given factor, typically used to adjust for display scaling.
     ///
@@ -1346,6 +1347,30 @@ impl Bounds<Pixels> {
             size: self.size.scale(factor),
         }
     }
+
+    /// Convert the bounds from logical pixels to physical pixels
+    pub fn to_device_pixels(&self, factor: f32) -> Bounds<DevicePixels> {
+        Bounds {
+            origin: point(
+                DevicePixels((self.origin.x.0 * factor) as i32),
+                DevicePixels((self.origin.y.0 * factor) as i32),
+            ),
+            size: self.size.to_device_pixels(factor),
+        }
+    }
+}
+
+impl Bounds<DevicePixels> {
+    /// Convert the bounds from physical pixels to logical pixels
+    pub fn to_pixels(self, scale_factor: f32) -> Bounds<Pixels> {
+        Bounds {
+            origin: point(
+                px(self.origin.x.0 as f32 / scale_factor),
+                px(self.origin.y.0 as f32 / scale_factor),
+            ),
+            size: self.size.to_pixels(scale_factor),
+        }
+    }
 }
 
 impl<T: Clone + Debug + Copy + Default> Copy for Bounds<T> {}

crates/gpui/src/platform.rs 🔗

@@ -187,12 +187,12 @@ pub trait PlatformDisplay: Send + Sync + Debug {
     fn uuid(&self) -> Result<Uuid>;
 
     /// Get the bounds for this display
-    fn bounds(&self) -> Bounds<DevicePixels>;
+    fn bounds(&self) -> Bounds<Pixels>;
 
     /// Get the default bounds for this display to place a window
-    fn default_bounds(&self) -> Bounds<DevicePixels> {
+    fn default_bounds(&self) -> Bounds<Pixels> {
         let center = self.bounds().center();
-        let offset = DEFAULT_WINDOW_SIZE / 2;
+        let offset = DEFAULT_WINDOW_SIZE / 2.0;
         let origin = point(center.x - offset.width, center.y - offset.height);
         Bounds::new(origin, DEFAULT_WINDOW_SIZE)
     }
@@ -211,7 +211,7 @@ impl Debug for DisplayId {
 unsafe impl Send for DisplayId {}
 
 pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
-    fn bounds(&self) -> Bounds<DevicePixels>;
+    fn bounds(&self) -> Bounds<Pixels>;
     fn is_maximized(&self) -> bool;
     fn window_bounds(&self) -> WindowBounds;
     fn content_size(&self) -> Size<Pixels>;
@@ -569,7 +569,7 @@ pub struct WindowOptions {
 /// The variables that can be configured when creating a new window
 #[derive(Debug)]
 pub(crate) struct WindowParams {
-    pub bounds: Bounds<DevicePixels>,
+    pub bounds: Bounds<Pixels>,
 
     /// The titlebar configuration of the window
     pub titlebar: Option<TitlebarOptions>,
@@ -597,13 +597,13 @@ pub(crate) struct WindowParams {
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub enum WindowBounds {
     /// Indicates that the window should open in a windowed state with the given bounds.
-    Windowed(Bounds<DevicePixels>),
+    Windowed(Bounds<Pixels>),
     /// Indicates that the window should open in a maximized state.
     /// The bounds provided here represent the restore size of the window.
-    Maximized(Bounds<DevicePixels>),
+    Maximized(Bounds<Pixels>),
     /// Indicates that the window should open in fullscreen mode.
     /// The bounds provided here represent the restore size of the window.
-    Fullscreen(Bounds<DevicePixels>),
+    Fullscreen(Bounds<Pixels>),
 }
 
 impl Default for WindowBounds {
@@ -614,7 +614,7 @@ impl Default for WindowBounds {
 
 impl WindowBounds {
     /// Retrieve the inner bounds
-    pub fn get_bounds(&self) -> Bounds<DevicePixels> {
+    pub fn get_bounds(&self) -> Bounds<Pixels> {
         match self {
             WindowBounds::Windowed(bounds) => *bounds,
             WindowBounds::Maximized(bounds) => *bounds,

crates/gpui/src/platform/blade/blade_renderer.rs 🔗

@@ -3,8 +3,8 @@
 
 use super::{BladeAtlas, PATH_TEXTURE_FORMAT};
 use crate::{
-    AtlasTextureKind, AtlasTile, Bounds, ContentMask, Hsla, MonochromeSprite, Path, PathId,
-    PathVertex, PolychromeSprite, PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Size,
+    AtlasTextureKind, AtlasTile, Bounds, ContentMask, DevicePixels, Hsla, MonochromeSprite, Path,
+    PathId, PathVertex, PolychromeSprite, PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Size,
     Underline,
 };
 use bytemuck::{Pod, Zeroable};
@@ -417,10 +417,10 @@ impl BladeRenderer {
         }
     }
 
-    pub fn update_drawable_size(&mut self, size: Size<f64>) {
+    pub fn update_drawable_size(&mut self, size: Size<DevicePixels>) {
         let gpu_size = gpu::Extent {
-            width: size.width as u32,
-            height: size.height as u32,
+            width: size.width.0 as u32,
+            height: size.height.0 as u32,
             depth: 1,
         };
 

crates/gpui/src/platform/linux/wayland/client.rs 🔗

@@ -557,7 +557,7 @@ impl LinuxClient for WaylandClient {
                 Rc::new(WaylandDisplay {
                     id: id.clone(),
                     name: output.name.clone(),
-                    bounds: output.bounds,
+                    bounds: output.bounds.to_pixels(output.scale as f32),
                 }) as Rc<dyn PlatformDisplay>
             })
             .collect()
@@ -573,7 +573,7 @@ impl LinuxClient for WaylandClient {
                     Rc::new(WaylandDisplay {
                         id: object_id.clone(),
                         name: output.name.clone(),
-                        bounds: output.bounds,
+                        bounds: output.bounds.to_pixels(output.scale as f32),
                     }) as Rc<dyn PlatformDisplay>
                 })
             })

crates/gpui/src/platform/linux/wayland/display.rs 🔗

@@ -6,14 +6,14 @@ use std::{
 use uuid::Uuid;
 use wayland_backend::client::ObjectId;
 
-use crate::{Bounds, DevicePixels, DisplayId, PlatformDisplay};
+use crate::{Bounds, DisplayId, Pixels, PlatformDisplay};
 
 #[derive(Debug, Clone)]
 pub(crate) struct WaylandDisplay {
     /// The ID of the wl_output object
     pub id: ObjectId,
     pub name: Option<String>,
-    pub bounds: Bounds<DevicePixels>,
+    pub bounds: Bounds<Pixels>,
 }
 
 impl Hash for WaylandDisplay {
@@ -35,7 +35,7 @@ impl PlatformDisplay for WaylandDisplay {
         }
     }
 
-    fn bounds(&self) -> Bounds<DevicePixels> {
+    fn bounds(&self) -> Bounds<Pixels> {
         self.bounds
     }
 }

crates/gpui/src/platform/linux/wayland/window.rs 🔗

@@ -1,6 +1,5 @@
 use std::cell::{Ref, RefCell, RefMut};
 use std::ffi::c_void;
-use std::num::NonZeroU32;
 use std::ptr::NonNull;
 use std::rc::Rc;
 use std::sync::Arc;
@@ -26,9 +25,9 @@ use crate::platform::linux::wayland::serial::SerialKind;
 use crate::platform::{PlatformAtlas, PlatformInputHandler, PlatformWindow};
 use crate::scene::Scene;
 use crate::{
-    px, size, AnyWindowHandle, Bounds, DevicePixels, Globals, Modifiers, Output, Pixels,
-    PlatformDisplay, PlatformInput, Point, PromptLevel, Size, WaylandClientStatePtr,
-    WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowParams,
+    px, size, AnyWindowHandle, Bounds, Globals, Modifiers, Output, Pixels, PlatformDisplay,
+    PlatformInput, Point, PromptLevel, Size, WaylandClientStatePtr, WindowAppearance,
+    WindowBackgroundAppearance, WindowBounds, WindowParams,
 };
 
 #[derive(Default)]
@@ -76,13 +75,13 @@ pub struct WaylandWindowState {
     display: Option<(ObjectId, Output)>,
     globals: Globals,
     renderer: BladeRenderer,
-    bounds: Bounds<u32>,
+    bounds: Bounds<Pixels>,
     scale: f32,
     input_handler: Option<PlatformInputHandler>,
     decoration_state: WaylandDecorationState,
     fullscreen: bool,
     maximized: bool,
-    windowed_bounds: Bounds<DevicePixels>,
+    windowed_bounds: Bounds<Pixels>,
     client: WaylandClientStatePtr,
     handle: AnyWindowHandle,
     active: bool,
@@ -108,8 +107,6 @@ impl WaylandWindowState {
         globals: Globals,
         options: WindowParams,
     ) -> anyhow::Result<Self> {
-        let bounds = options.bounds.map(|p| p.0 as u32);
-
         let raw = RawWindow {
             window: surface.id().as_ptr().cast::<c_void>(),
             display: surface
@@ -134,8 +131,8 @@ impl WaylandWindowState {
         );
         let config = BladeSurfaceConfig {
             size: gpu::Extent {
-                width: bounds.size.width,
-                height: bounds.size.height,
+                width: options.bounds.size.width.0 as u32,
+                height: options.bounds.size.height.0 as u32,
                 depth: 1,
             },
             transparent: options.window_background != WindowBackgroundAppearance::Opaque,
@@ -153,7 +150,7 @@ impl WaylandWindowState {
             outputs: HashMap::default(),
             display: None,
             renderer: BladeRenderer::new(gpu, config),
-            bounds,
+            bounds: options.bounds,
             scale: 1.0,
             input_handler: None,
             decoration_state: WaylandDecorationState::Client,
@@ -349,10 +346,16 @@ impl WaylandWindowStatePtr {
     pub fn handle_toplevel_event(&self, event: xdg_toplevel::Event) -> bool {
         match event {
             xdg_toplevel::Event::Configure {
-                mut width,
-                mut height,
+                width,
+                height,
                 states,
             } => {
+                let mut size = if width == 0 || height == 0 {
+                    None
+                } else {
+                    Some(size(px(width as f32), px(height as f32)))
+                };
+
                 let fullscreen = states.contains(&(xdg_toplevel::State::Fullscreen as u8));
                 let maximized = states.contains(&(xdg_toplevel::State::Maximized as u8));
 
@@ -362,19 +365,20 @@ impl WaylandWindowStatePtr {
                 state.maximized = maximized;
 
                 if got_unmaximized {
-                    width = state.windowed_bounds.size.width.0;
-                    height = state.windowed_bounds.size.height.0;
-                } else if width != 0 && height != 0 && !fullscreen && !maximized {
-                    state.windowed_bounds = Bounds {
-                        origin: Point::default(),
-                        size: size(width.into(), height.into()),
-                    };
+                    size = Some(state.windowed_bounds.size);
+                } else if !fullscreen && !maximized {
+                    if let Some(size) = size {
+                        state.windowed_bounds = Bounds {
+                            origin: Point::default(),
+                            size,
+                        };
+                    }
                 }
 
-                let width = NonZeroU32::new(width as u32);
-                let height = NonZeroU32::new(height as u32);
                 drop(state);
-                self.resize(width, height);
+                if let Some(size) = size {
+                    self.resize(size);
+                }
 
                 false
             }
@@ -483,63 +487,43 @@ impl WaylandWindowStatePtr {
         bounds
     }
 
-    pub fn set_size_and_scale(
-        &self,
-        width: Option<NonZeroU32>,
-        height: Option<NonZeroU32>,
-        scale: Option<f32>,
-    ) {
-        let (width, height, scale) = {
+    pub fn set_size_and_scale(&self, size: Option<Size<Pixels>>, scale: Option<f32>) {
+        let (size, scale) = {
             let mut state = self.state.borrow_mut();
-            if width.map_or(true, |width| width.get() == state.bounds.size.width)
-                && height.map_or(true, |height| height.get() == state.bounds.size.height)
+            if size.map_or(true, |size| size == state.bounds.size)
                 && scale.map_or(true, |scale| scale == state.scale)
             {
                 return;
             }
-            if let Some(width) = width {
-                state.bounds.size.width = width.get();
-            }
-            if let Some(height) = height {
-                state.bounds.size.height = height.get();
+            if let Some(size) = size {
+                state.bounds.size = size;
             }
             if let Some(scale) = scale {
                 state.scale = scale;
             }
-            let width = state.bounds.size.width;
-            let height = state.bounds.size.height;
-            let scale = state.scale;
-            state.renderer.update_drawable_size(size(
-                width as f64 * scale as f64,
-                height as f64 * scale as f64,
-            ));
-            (width, height, scale)
+            let device_bounds = state.bounds.to_device_pixels(state.scale);
+            state.renderer.update_drawable_size(device_bounds.size);
+            (state.bounds.size, state.scale)
         };
 
         if let Some(ref mut fun) = self.callbacks.borrow_mut().resize {
-            fun(
-                Size {
-                    width: px(width as f32),
-                    height: px(height as f32),
-                },
-                scale,
-            );
+            fun(size, scale);
         }
 
         {
             let state = self.state.borrow();
             if let Some(viewport) = &state.viewport {
-                viewport.set_destination(width as i32, height as i32);
+                viewport.set_destination(size.width.0 as i32, size.height.0 as i32);
             }
         }
     }
 
-    pub fn resize(&self, width: Option<NonZeroU32>, height: Option<NonZeroU32>) {
-        self.set_size_and_scale(width, height, None);
+    pub fn resize(&self, size: Size<Pixels>) {
+        self.set_size_and_scale(Some(size), None);
     }
 
     pub fn rescale(&self, scale: f32) {
-        self.set_size_and_scale(None, None, Some(scale));
+        self.set_size_and_scale(None, Some(scale));
     }
 
     /// Notifies the window of the state of the decorations.
@@ -625,8 +609,8 @@ impl rwh::HasDisplayHandle for WaylandWindow {
 }
 
 impl PlatformWindow for WaylandWindow {
-    fn bounds(&self) -> Bounds<DevicePixels> {
-        self.borrow().bounds.map(|p| DevicePixels(p as i32))
+    fn bounds(&self) -> Bounds<Pixels> {
+        self.borrow().bounds
     }
 
     fn is_maximized(&self) -> bool {
@@ -640,16 +624,13 @@ impl PlatformWindow for WaylandWindow {
         } else if state.maximized {
             WindowBounds::Maximized(state.windowed_bounds)
         } else {
-            WindowBounds::Windowed(state.bounds.map(|p| DevicePixels(p as i32)))
+            drop(state);
+            WindowBounds::Windowed(self.bounds())
         }
     }
 
     fn content_size(&self) -> Size<Pixels> {
-        let state = self.borrow();
-        Size {
-            width: Pixels(state.bounds.size.width as f32),
-            height: Pixels(state.bounds.size.height as f32),
-        }
+        self.borrow().bounds.size
     }
 
     fn scale_factor(&self) -> f32 {
@@ -661,11 +642,12 @@ impl PlatformWindow for WaylandWindow {
     }
 
     fn display(&self) -> Option<Rc<dyn PlatformDisplay>> {
-        self.borrow().display.as_ref().map(|(id, display)| {
+        let state = self.borrow();
+        state.display.as_ref().map(|(id, display)| {
             Rc::new(WaylandDisplay {
                 id: id.clone(),
                 name: display.name.clone(),
-                bounds: display.bounds,
+                bounds: display.bounds.to_pixels(state.scale),
             }) as Rc<dyn PlatformDisplay>
         })
     }

crates/gpui/src/platform/linux/x11/client.rs 🔗

@@ -908,8 +908,11 @@ impl LinuxClient for X11Client {
             .iter()
             .enumerate()
             .filter_map(|(root_id, _)| {
-                Some(Rc::new(X11Display::new(&state.xcb_connection, root_id)?)
-                    as Rc<dyn PlatformDisplay>)
+                Some(Rc::new(X11Display::new(
+                    &state.xcb_connection,
+                    state.scale_factor,
+                    root_id,
+                )?) as Rc<dyn PlatformDisplay>)
             })
             .collect()
     }
@@ -918,8 +921,12 @@ impl LinuxClient for X11Client {
         let state = self.0.borrow();
 
         Some(Rc::new(
-            X11Display::new(&state.xcb_connection, state.x_root_index)
-                .expect("There should always be a root index"),
+            X11Display::new(
+                &state.xcb_connection,
+                state.scale_factor,
+                state.x_root_index,
+            )
+            .expect("There should always be a root index"),
         ))
     }
 
@@ -928,6 +935,7 @@ impl LinuxClient for X11Client {
 
         Some(Rc::new(X11Display::new(
             &state.xcb_connection,
+            state.scale_factor,
             id.0 as usize,
         )?))
     }

crates/gpui/src/platform/linux/x11/display.rs 🔗

@@ -2,25 +2,29 @@ use anyhow::Result;
 use uuid::Uuid;
 use x11rb::{connection::Connection as _, xcb_ffi::XCBConnection};
 
-use crate::{Bounds, DevicePixels, DisplayId, PlatformDisplay, Size};
+use crate::{px, Bounds, DisplayId, Pixels, PlatformDisplay, Size};
 
 #[derive(Debug)]
 pub(crate) struct X11Display {
     x_screen_index: usize,
-    bounds: Bounds<DevicePixels>,
+    bounds: Bounds<Pixels>,
     uuid: Uuid,
 }
 
 impl X11Display {
-    pub(crate) fn new(xc: &XCBConnection, x_screen_index: usize) -> Option<Self> {
+    pub(crate) fn new(
+        xc: &XCBConnection,
+        scale_factor: f32,
+        x_screen_index: usize,
+    ) -> Option<Self> {
         let screen = xc.setup().roots.get(x_screen_index).unwrap();
         Some(Self {
-            x_screen_index: x_screen_index,
+            x_screen_index,
             bounds: Bounds {
                 origin: Default::default(),
                 size: Size {
-                    width: DevicePixels(screen.width_in_pixels as i32),
-                    height: DevicePixels(screen.height_in_pixels as i32),
+                    width: px(screen.width_in_pixels as f32 / scale_factor),
+                    height: px(screen.height_in_pixels as f32 / scale_factor),
                 },
             },
             uuid: Uuid::from_bytes([0; 16]),
@@ -37,7 +41,7 @@ impl PlatformDisplay for X11Display {
         Ok(self.uuid)
     }
 
-    fn bounds(&self) -> Bounds<DevicePixels> {
+    fn bounds(&self) -> Bounds<Pixels> {
         self.bounds
     }
 }

crates/gpui/src/platform/linux/x11/window.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{
     platform::blade::{BladeRenderer, BladeSurfaceConfig},
-    size, AnyWindowHandle, Bounds, DevicePixels, ForegroundExecutor, Modifiers, Pixels,
+    px, size, AnyWindowHandle, Bounds, DevicePixels, ForegroundExecutor, Modifiers, Pixels,
     PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point,
     PromptLevel, Scene, Size, WindowAppearance, WindowBackgroundAppearance, WindowBounds,
     WindowKind, WindowParams, X11ClientStatePtr,
@@ -162,7 +162,7 @@ pub struct X11WindowState {
     atoms: XcbAtoms,
     x_root_window: xproto::Window,
     _raw: RawWindow,
-    bounds: Bounds<i32>,
+    bounds: Bounds<Pixels>,
     scale_factor: f32,
     renderer: BladeRenderer,
     display: Rc<dyn PlatformDisplay>,
@@ -273,10 +273,10 @@ impl X11WindowState {
                 visual.depth,
                 x_window,
                 visual_set.root,
-                params.bounds.origin.x.0 as i16,
-                params.bounds.origin.y.0 as i16,
-                params.bounds.size.width.0 as u16,
-                params.bounds.size.height.0 as u16,
+                (params.bounds.origin.x.0 * scale_factor) as i16,
+                (params.bounds.origin.y.0 * scale_factor) as i16,
+                (params.bounds.size.width.0 * scale_factor) as u16,
+                (params.bounds.size.height.0 * scale_factor) as u16,
                 0,
                 xproto::WindowClass::INPUT_OUTPUT,
                 visual.id,
@@ -370,10 +370,12 @@ impl X11WindowState {
         Ok(Self {
             client,
             executor,
-            display: Rc::new(X11Display::new(xcb_connection, x_screen_index).unwrap()),
+            display: Rc::new(
+                X11Display::new(xcb_connection, scale_factor, x_screen_index).unwrap(),
+            ),
             _raw: raw,
             x_root_window: visual_set.root,
-            bounds: params.bounds.map(|v| v.0),
+            bounds: params.bounds,
             scale_factor,
             renderer: BladeRenderer::new(gpu, config),
             atoms: *atoms,
@@ -627,6 +629,7 @@ impl X11WindowStatePtr {
         let is_resize;
         {
             let mut state = self.state.borrow_mut();
+            let bounds = bounds.map(|f| px(f as f32 / state.scale_factor));
 
             is_resize = bounds.size.width != state.bounds.size.width
                 || bounds.size.height != state.bounds.size.height;
@@ -641,9 +644,10 @@ impl X11WindowStatePtr {
 
             let gpu_size = query_render_extent(&self.xcb_connection, self.x_window);
             if state.renderer.viewport_size() != gpu_size {
-                state
-                    .renderer
-                    .update_drawable_size(size(gpu_size.width as f64, gpu_size.height as f64));
+                state.renderer.update_drawable_size(size(
+                    DevicePixels(gpu_size.width as i32),
+                    DevicePixels(gpu_size.height as i32),
+                ));
                 resize_args = Some((state.content_size(), state.scale_factor));
             }
         }
@@ -678,8 +682,8 @@ impl X11WindowStatePtr {
 }
 
 impl PlatformWindow for X11Window {
-    fn bounds(&self) -> Bounds<DevicePixels> {
-        self.0.state.borrow().bounds.map(|v| v.into())
+    fn bounds(&self) -> Bounds<Pixels> {
+        self.0.state.borrow().bounds
     }
 
     fn is_maximized(&self) -> bool {
@@ -693,7 +697,11 @@ impl PlatformWindow for X11Window {
 
     fn window_bounds(&self) -> WindowBounds {
         let state = self.0.state.borrow();
-        WindowBounds::Windowed(state.bounds.map(|p| DevicePixels(p)))
+        if self.is_maximized() {
+            WindowBounds::Maximized(state.bounds)
+        } else {
+            WindowBounds::Windowed(state.bounds)
+        }
     }
 
     fn content_size(&self) -> Size<Pixels> {

crates/gpui/src/platform/mac/display.rs 🔗

@@ -1,4 +1,4 @@
-use crate::{point, size, Bounds, DevicePixels, DisplayId, PlatformDisplay};
+use crate::{px, size, Bounds, DisplayId, Pixels, PlatformDisplay};
 use anyhow::Result;
 use cocoa::{
     appkit::NSScreen,
@@ -102,18 +102,15 @@ impl PlatformDisplay for MacDisplay {
         ]))
     }
 
-    fn bounds(&self) -> Bounds<DevicePixels> {
+    fn bounds(&self) -> Bounds<Pixels> {
         unsafe {
             // CGDisplayBounds is in "global display" coordinates, where 0 is
             // the top left of the primary display.
             let bounds = CGDisplayBounds(self.0);
 
             Bounds {
-                origin: point(DevicePixels(0), DevicePixels(0)),
-                size: size(
-                    DevicePixels(bounds.size.width as i32),
-                    DevicePixels(bounds.size.height as i32),
-                ),
+                origin: Default::default(),
+                size: size(px(bounds.size.width as f32), px(bounds.size.height as f32)),
             }
         }
     }

crates/gpui/src/platform/mac/metal_renderer.rs 🔗

@@ -8,7 +8,7 @@ use anyhow::{anyhow, Result};
 use block::ConcreteBlock;
 use cocoa::{
     base::{NO, YES},
-    foundation::NSUInteger,
+    foundation::{NSSize, NSUInteger},
     quartzcore::AutoresizingMask,
 };
 use collections::HashMap;
@@ -268,7 +268,11 @@ impl MetalRenderer {
             .set_presents_with_transaction(presents_with_transaction);
     }
 
-    pub fn update_drawable_size(&mut self, size: Size<f64>) {
+    pub fn update_drawable_size(&mut self, size: Size<DevicePixels>) {
+        let size = NSSize {
+            width: size.width.0 as f64,
+            height: size.height.0 as f64,
+        };
         unsafe {
             let _: () = msg_send![
                 self.layer(),

crates/gpui/src/platform/mac/window.rs 🔗

@@ -1,11 +1,10 @@
 use super::{ns_string, renderer, MacDisplay, NSRange, NSStringExt};
 use crate::{
-    platform::PlatformInputHandler, point, px, size, AnyWindowHandle, Bounds, DevicePixels,
-    DisplayLink, ExternalPaths, FileDropEvent, ForegroundExecutor, KeyDownEvent, Keystroke,
-    Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
-    Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptLevel,
-    Size, Timer, WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowKind,
-    WindowParams,
+    platform::PlatformInputHandler, point, px, size, AnyWindowHandle, Bounds, DisplayLink,
+    ExternalPaths, FileDropEvent, ForegroundExecutor, KeyDownEvent, Keystroke, Modifiers,
+    ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels,
+    PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptLevel, Size, Timer,
+    WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowKind, WindowParams,
 };
 use block::ConcreteBlock;
 use cocoa::{
@@ -345,7 +344,7 @@ struct MacWindowState {
     external_files_dragged: bool,
     // Whether the next left-mouse click is also the focusing click.
     first_mouse: bool,
-    fullscreen_restore_bounds: Bounds<DevicePixels>,
+    fullscreen_restore_bounds: Bounds<Pixels>,
 }
 
 impl MacWindowState {
@@ -439,7 +438,7 @@ impl MacWindowState {
         }
     }
 
-    fn bounds(&self) -> Bounds<DevicePixels> {
+    fn bounds(&self) -> Bounds<Pixels> {
         let mut window_frame = unsafe { NSWindow::frame(self.native_window) };
         let screen_frame = unsafe {
             let screen = NSWindow::screen(self.native_window);
@@ -452,12 +451,12 @@ impl MacWindowState {
 
         let bounds = Bounds::new(
             point(
-                ((window_frame.origin.x - screen_frame.origin.x) as i32).into(),
-                ((window_frame.origin.y - screen_frame.origin.y) as i32).into(),
+                px((window_frame.origin.x - screen_frame.origin.x) as f32),
+                px((window_frame.origin.y - screen_frame.origin.y) as f32),
             ),
             size(
-                (window_frame.size.width as i32).into(),
-                (window_frame.size.height as i32).into(),
+                px(window_frame.size.width as f32),
+                px(window_frame.size.height as f32),
             ),
         );
         bounds
@@ -473,13 +472,6 @@ impl MacWindowState {
         get_scale_factor(self.native_window)
     }
 
-    fn update_drawable_size(&mut self, drawable_size: NSSize) {
-        self.renderer.update_drawable_size(Size {
-            width: drawable_size.width,
-            height: drawable_size.height,
-        })
-    }
-
     fn titlebar_height(&self) -> Pixels {
         unsafe {
             let frame = NSWindow::frame(self.native_window);
@@ -599,14 +591,6 @@ impl MacWindow {
             let native_view = NSView::init(native_view);
             assert!(!native_view.is_null());
 
-            let window_size = {
-                let scale = get_scale_factor(native_window);
-                size(
-                    bounds.size.width.0 as f32 * scale,
-                    bounds.size.height.0 as f32 * scale,
-                )
-            };
-
             let mut window = Self(Arc::new(Mutex::new(MacWindowState {
                 handle,
                 executor,
@@ -617,7 +601,7 @@ impl MacWindow {
                     renderer_context,
                     native_window as *mut _,
                     native_view as *mut _,
-                    window_size,
+                    bounds.size.map(|pixels| pixels.0),
                     window_background != WindowBackgroundAppearance::Opaque,
                 ),
                 request_frame_callback: None,
@@ -772,7 +756,7 @@ impl Drop for MacWindow {
 }
 
 impl PlatformWindow for MacWindow {
-    fn bounds(&self) -> Bounds<DevicePixels> {
+    fn bounds(&self) -> Bounds<Pixels> {
         self.0.as_ref().lock().bounds()
     }
 
@@ -1580,20 +1564,17 @@ 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() as f64;
+    let scale_factor = lock.scale_factor();
     let size = lock.content_size();
-    let drawable_size: NSSize = NSSize {
-        width: f64::from(size.width) * scale_factor,
-        height: f64::from(size.height) * scale_factor,
-    };
+    let drawable_size = size.to_device_pixels(scale_factor);
     unsafe {
         let _: () = msg_send![
             lock.renderer.layer(),
-            setContentsScale: scale_factor
+            setContentsScale: scale_factor as f64
         ];
     }
 
-    lock.update_drawable_size(drawable_size);
+    lock.renderer.update_drawable_size(drawable_size);
 
     if let Some(mut callback) = lock.resize_callback.take() {
         let content_size = lock.content_size();
@@ -1608,7 +1589,8 @@ extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
     let window_state = unsafe { get_window_state(this) };
     let mut lock = window_state.as_ref().lock();
 
-    if lock.content_size() == size.into() {
+    let new_size = Size::<Pixels>::from(size);
+    if lock.content_size() == new_size {
         return;
     }
 
@@ -1616,16 +1598,10 @@ extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
         let _: () = msg_send![super(this, class!(NSView)), setFrameSize: size];
     }
 
-    let scale_factor = lock.scale_factor() as f64;
-    let drawable_size: NSSize = NSSize {
-        width: size.width * scale_factor,
-        height: size.height * scale_factor,
-    };
-
-    lock.update_drawable_size(drawable_size);
+    let scale_factor = lock.scale_factor();
+    let drawable_size = new_size.to_device_pixels(scale_factor);
+    lock.renderer.update_drawable_size(drawable_size);
 
-    drop(lock);
-    let mut lock = window_state.lock();
     if let Some(mut callback) = lock.resize_callback.take() {
         let content_size = lock.content_size();
         let scale_factor = lock.scale_factor();

crates/gpui/src/platform/test/display.rs 🔗

@@ -1,12 +1,11 @@
+use crate::{px, Bounds, DisplayId, Pixels, PlatformDisplay, Point};
 use anyhow::{Ok, Result};
 
-use crate::{Bounds, DevicePixels, DisplayId, PlatformDisplay, Point};
-
 #[derive(Debug)]
 pub(crate) struct TestDisplay {
     id: DisplayId,
     uuid: uuid::Uuid,
-    bounds: Bounds<DevicePixels>,
+    bounds: Bounds<Pixels>,
 }
 
 impl TestDisplay {
@@ -14,10 +13,7 @@ impl TestDisplay {
         TestDisplay {
             id: DisplayId(1),
             uuid: uuid::Uuid::new_v4(),
-            bounds: Bounds::from_corners(
-                Point::default(),
-                Point::new(DevicePixels(1920), DevicePixels(1080)),
-            ),
+            bounds: Bounds::from_corners(Point::default(), Point::new(px(1920.), px(1080.))),
         }
     }
 }
@@ -31,7 +27,7 @@ impl PlatformDisplay for TestDisplay {
         Ok(self.uuid)
     }
 
-    fn bounds(&self) -> crate::Bounds<crate::DevicePixels> {
+    fn bounds(&self) -> crate::Bounds<crate::Pixels> {
         self.bounds
     }
 }

crates/gpui/src/platform/test/window.rs 🔗

@@ -1,8 +1,8 @@
 use crate::{
-    AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, DevicePixels,
-    DispatchEventResult, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
-    PlatformInputHandler, PlatformWindow, Point, Size, TestPlatform, TileId, WindowAppearance,
-    WindowBackgroundAppearance, WindowBounds, WindowParams,
+    AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, DispatchEventResult, Pixels,
+    PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point,
+    Size, TestPlatform, TileId, WindowAppearance, WindowBackgroundAppearance, WindowBounds,
+    WindowParams,
 };
 use collections::HashMap;
 use parking_lot::Mutex;
@@ -13,7 +13,7 @@ use std::{
 };
 
 pub(crate) struct TestWindowState {
-    pub(crate) bounds: Bounds<DevicePixels>,
+    pub(crate) bounds: Bounds<Pixels>,
     pub(crate) handle: AnyWindowHandle,
     display: Rc<dyn PlatformDisplay>,
     pub(crate) title: Option<String>,
@@ -79,7 +79,7 @@ impl TestWindow {
         let Some(mut callback) = lock.resize_callback.take() else {
             return;
         };
-        lock.bounds.size = size.map(|pixels| (pixels.0 as i32).into());
+        lock.bounds.size = size;
         drop(lock);
         callback(size, scale_factor);
         self.0.lock().resize_callback = Some(callback);
@@ -108,7 +108,7 @@ impl TestWindow {
 }
 
 impl PlatformWindow for TestWindow {
-    fn bounds(&self) -> Bounds<DevicePixels> {
+    fn bounds(&self) -> Bounds<Pixels> {
         self.0.lock().bounds
     }
 
@@ -121,7 +121,7 @@ impl PlatformWindow for TestWindow {
     }
 
     fn content_size(&self) -> Size<Pixels> {
-        self.bounds().size.into()
+        self.bounds().size
     }
 
     fn scale_factor(&self) -> f32 {

crates/gpui/src/platform/windows/display.rs 🔗

@@ -8,13 +8,13 @@ use windows::{
     Win32::{Foundation::*, Graphics::Gdi::*},
 };
 
-use crate::{Bounds, DevicePixels, DisplayId, PlatformDisplay, Point, Size};
+use crate::{px, Bounds, DisplayId, Pixels, PlatformDisplay, Point, Size};
 
 #[derive(Debug, Clone, Copy)]
 pub(crate) struct WindowsDisplay {
     pub handle: HMONITOR,
     pub display_id: DisplayId,
-    bounds: Bounds<DevicePixels>,
+    bounds: Bounds<Pixels>,
     uuid: Uuid,
 }
 
@@ -34,12 +34,12 @@ impl WindowsDisplay {
             display_id,
             bounds: Bounds {
                 origin: Point {
-                    x: DevicePixels(size.left),
-                    y: DevicePixels(size.top),
+                    x: px(size.left as f32),
+                    y: px(size.top as f32),
                 },
                 size: Size {
-                    width: DevicePixels(size.right - size.left),
-                    height: DevicePixels(size.bottom - size.top),
+                    width: px((size.right - size.left) as f32),
+                    height: px((size.bottom - size.top) as f32),
                 },
             },
             uuid,
@@ -60,12 +60,12 @@ impl WindowsDisplay {
             display_id: DisplayId(display_id as _),
             bounds: Bounds {
                 origin: Point {
-                    x: DevicePixels(size.left as i32),
-                    y: DevicePixels(size.top as i32),
+                    x: px(size.left as f32),
+                    y: px(size.top as f32),
                 },
                 size: Size {
-                    width: DevicePixels((size.right - size.left) as i32),
-                    height: DevicePixels((size.bottom - size.top) as i32),
+                    width: px((size.right - size.left) as f32),
+                    height: px((size.bottom - size.top) as f32),
                 },
             },
             uuid,
@@ -82,12 +82,12 @@ impl WindowsDisplay {
             display_id,
             bounds: Bounds {
                 origin: Point {
-                    x: DevicePixels(size.left as i32),
-                    y: DevicePixels(size.top as i32),
+                    x: px(size.left as f32),
+                    y: px(size.top as f32),
                 },
                 size: Size {
-                    width: DevicePixels((size.right - size.left) as i32),
-                    height: DevicePixels((size.bottom - size.top) as i32),
+                    width: px((size.right - size.left) as f32),
+                    height: px((size.bottom - size.top) as f32),
                 },
             },
             uuid,
@@ -109,11 +109,11 @@ impl WindowsDisplay {
     }
 
     /// Check if the center point of given bounds is inside this monitor
-    pub fn check_given_bounds(&self, bounds: Bounds<DevicePixels>) -> bool {
+    pub fn check_given_bounds(&self, bounds: Bounds<Pixels>) -> bool {
         let center = bounds.center();
         let center = POINT {
-            x: center.x.0,
-            y: center.y.0,
+            x: center.x.0 as i32,
+            y: center.y.0 as i32,
         };
         let monitor = unsafe { MonitorFromPoint(center, MONITOR_DEFAULTTONULL) };
         if monitor.is_invalid() {
@@ -167,7 +167,7 @@ impl PlatformDisplay for WindowsDisplay {
         Ok(self.uuid)
     }
 
-    fn bounds(&self) -> Bounds<DevicePixels> {
+    fn bounds(&self) -> Bounds<Pixels> {
         self.bounds
     }
 }

crates/gpui/src/platform/windows/events.rs 🔗

@@ -96,13 +96,13 @@ fn handle_move_msg(
     lparam: LPARAM,
     state_ptr: Rc<WindowsWindowStatePtr>,
 ) -> Option<isize> {
-    let x = lparam.signed_loword() as i32;
-    let y = lparam.signed_hiword() as i32;
+    let x = lparam.signed_loword() as f32;
+    let y = lparam.signed_hiword() as f32;
     let mut lock = state_ptr.state.borrow_mut();
-    lock.origin = point(x.into(), y.into());
+    lock.origin = point(px(x), px(y));
     let size = lock.physical_size;
-    let center_x = x + size.width.0 / 2;
-    let center_y = y + size.height.0 / 2;
+    let center_x = x + size.width.0 / 2.;
+    let center_y = y + size.height.0 / 2.;
     let monitor_bounds = lock.display.bounds();
     if center_x < monitor_bounds.left().0
         || center_x > monitor_bounds.right().0
@@ -129,18 +129,15 @@ fn handle_move_msg(
 fn handle_size_msg(lparam: LPARAM, state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
     let width = lparam.loword().max(1) as i32;
     let height = lparam.hiword().max(1) as i32;
-    let new_physical_size = size(width.into(), height.into());
     let mut lock = state_ptr.state.borrow_mut();
+    let new_size = size(DevicePixels(width), DevicePixels(height));
     let scale_factor = lock.scale_factor;
-    lock.physical_size = new_physical_size;
-    lock.renderer.update_drawable_size(Size {
-        width: width as f64,
-        height: height as f64,
-    });
+    lock.renderer.update_drawable_size(new_size);
+    let new_size = new_size.to_pixels(lock.scale_factor);
+    lock.physical_size = new_size;
     if let Some(mut callback) = lock.callbacks.resize.take() {
         drop(lock);
-        let logical_size = logical_size(new_physical_size, scale_factor);
-        callback(logical_size, scale_factor);
+        callback(new_size, scale_factor);
         state_ptr.state.borrow_mut().callbacks.resize = Some(callback);
     }
     Some(0)

crates/gpui/src/platform/windows/window.rs 🔗

@@ -27,14 +27,14 @@ use windows::{
 };
 
 use crate::platform::blade::BladeRenderer;
-use crate::*;
+use crate::{Pixels, *};
 
 pub(crate) struct WindowsWindow(pub Rc<WindowsWindowStatePtr>);
 
 pub struct WindowsWindowState {
-    pub origin: Point<DevicePixels>,
-    pub physical_size: Size<DevicePixels>,
-    pub fullscreen_restore_bounds: Bounds<DevicePixels>,
+    pub origin: Point<Pixels>,
+    pub physical_size: Size<Pixels>,
+    pub fullscreen_restore_bounds: Bounds<Pixels>,
     pub scale_factor: f32,
 
     pub callbacks: Callbacks,
@@ -67,8 +67,8 @@ impl WindowsWindowState {
         current_cursor: HCURSOR,
         display: WindowsDisplay,
     ) -> Self {
-        let origin = point(cs.x.into(), cs.y.into());
-        let physical_size = size(cs.cx.into(), cs.cy.into());
+        let origin = point(px(cs.x as f32), px(cs.y as f32));
+        let physical_size = size(px(cs.cx as f32), px(cs.cy as f32));
         let fullscreen_restore_bounds = Bounds {
             origin,
             size: physical_size,
@@ -110,7 +110,7 @@ impl WindowsWindowState {
         !self.is_fullscreen() && unsafe { IsZoomed(self.hwnd) }.as_bool()
     }
 
-    fn bounds(&self) -> Bounds<DevicePixels> {
+    fn bounds(&self) -> Bounds<Pixels> {
         Bounds {
             origin: self.origin,
             size: self.physical_size,
@@ -128,12 +128,12 @@ impl WindowsWindowState {
         };
         let bounds = Bounds {
             origin: point(
-                DevicePixels(placement.rcNormalPosition.left),
-                DevicePixels(placement.rcNormalPosition.top),
+                px(placement.rcNormalPosition.left as f32),
+                px(placement.rcNormalPosition.top as f32),
             ),
             size: size(
-                DevicePixels(placement.rcNormalPosition.right - placement.rcNormalPosition.left),
-                DevicePixels(placement.rcNormalPosition.bottom - placement.rcNormalPosition.top),
+                px((placement.rcNormalPosition.right - placement.rcNormalPosition.left) as f32),
+                px((placement.rcNormalPosition.bottom - placement.rcNormalPosition.top) as f32),
             ),
         };
 
@@ -151,7 +151,7 @@ impl WindowsWindowState {
     /// Currently, GPUI uses logical size of the app to handle mouse interactions (such as
     /// whether the mouse collides with other elements of GPUI).
     fn content_size(&self) -> Size<Pixels> {
-        logical_size(self.physical_size, self.scale_factor)
+        self.physical_size
     }
 
     fn title_bar_padding(&self) -> Pixels {
@@ -303,6 +303,7 @@ impl WindowsWindow {
             } else {
                 display.default_bounds()
             };
+            let bounds = bounds.to_device_pixels(wnd.0.state.borrow().scale_factor);
             placement.rcNormalPosition.left = bounds.left().0;
             placement.rcNormalPosition.right = bounds.right().0;
             placement.rcNormalPosition.top = bounds.top().0;
@@ -350,7 +351,7 @@ impl Drop for WindowsWindow {
 }
 
 impl PlatformWindow for WindowsWindow {
-    fn bounds(&self) -> Bounds<DevicePixels> {
+    fn bounds(&self) -> Bounds<Pixels> {
         self.0.state.borrow().bounds()
     }
 
@@ -551,10 +552,10 @@ impl PlatformWindow for WindowsWindow {
                     unsafe { GetWindowRect(state_ptr.hwnd, &mut rc) }.log_err();
                     let _ = lock.fullscreen.insert(StyleAndBounds {
                         style,
-                        x: rc.left,
-                        y: rc.top,
-                        cx: rc.right - rc.left,
-                        cy: rc.bottom - rc.top,
+                        x: px(rc.left as f32),
+                        y: px(rc.top as f32),
+                        cx: px((rc.right - rc.left) as f32),
+                        cy: px((rc.bottom - rc.top) as f32),
                     });
                     let style = style
                         & !(WS_THICKFRAME
@@ -565,10 +566,10 @@ impl PlatformWindow for WindowsWindow {
                     let bounds = lock.display.bounds();
                     StyleAndBounds {
                         style,
-                        x: bounds.left().0,
-                        y: bounds.top().0,
-                        cx: bounds.size.width.0,
-                        cy: bounds.size.height.0,
+                        x: bounds.left(),
+                        y: bounds.top(),
+                        cx: bounds.size.width,
+                        cy: bounds.size.height,
                     }
                 };
                 drop(lock);
@@ -577,10 +578,10 @@ impl PlatformWindow for WindowsWindow {
                     SetWindowPos(
                         state_ptr.hwnd,
                         HWND::default(),
-                        x,
-                        y,
-                        cx,
-                        cy,
+                        x.0 as i32,
+                        y.0 as i32,
+                        cx.0 as i32,
+                        cy.0 as i32,
                         SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOZORDER,
                     )
                 }
@@ -833,10 +834,10 @@ impl ClickState {
 
 struct StyleAndBounds {
     style: WINDOW_STYLE,
-    x: i32,
-    y: i32,
-    cx: i32,
-    cy: i32,
+    x: Pixels,
+    y: Pixels,
+    cx: Pixels,
+    cy: Pixels,
 }
 
 fn register_wnd_class(icon_handle: HICON) -> PCWSTR {

crates/gpui/src/window.rs 🔗

@@ -52,8 +52,7 @@ mod prompts;
 
 pub use prompts::*;
 
-pub(crate) const DEFAULT_WINDOW_SIZE: Size<DevicePixels> =
-    size(DevicePixels(1024), DevicePixels(700));
+pub(crate) const DEFAULT_WINDOW_SIZE: Size<Pixels> = size(px(1024.), px(700.));
 
 /// Represents the two different phases when dispatching events.
 #[derive(Default, Copy, Clone, Debug, Eq, PartialEq)]
@@ -581,8 +580,8 @@ pub(crate) struct ElementStateBox {
     pub(crate) type_name: &'static str,
 }
 
-fn default_bounds(display_id: Option<DisplayId>, cx: &mut AppContext) -> Bounds<DevicePixels> {
-    const DEFAULT_WINDOW_OFFSET: Point<DevicePixels> = point(DevicePixels(0), DevicePixels(35));
+fn default_bounds(display_id: Option<DisplayId>, cx: &mut AppContext) -> Bounds<Pixels> {
+    const DEFAULT_WINDOW_OFFSET: Point<Pixels> = point(px(0.), px(35.));
 
     cx.active_window()
         .and_then(|w| w.update(cx, |_, cx| cx.bounds()).ok())
@@ -594,9 +593,7 @@ fn default_bounds(display_id: Option<DisplayId>, cx: &mut AppContext) -> Bounds<
 
             display
                 .map(|display| display.default_bounds())
-                .unwrap_or_else(|| {
-                    Bounds::new(point(DevicePixels(0), DevicePixels(0)), DEFAULT_WINDOW_SIZE)
-                })
+                .unwrap_or_else(|| Bounds::new(point(px(0.), px(0.)), DEFAULT_WINDOW_SIZE))
         })
 }
 
@@ -1145,7 +1142,7 @@ impl<'a> WindowContext<'a> {
     }
 
     /// Returns the bounds of the current window in the global coordinate space, which could span across multiple displays.
-    pub fn bounds(&self) -> Bounds<DevicePixels> {
+    pub fn bounds(&self) -> Bounds<Pixels> {
         self.window.platform_window.bounds()
     }
 

crates/workspace/src/persistence.rs 🔗

@@ -12,6 +12,7 @@ use sqlez::{
     statement::Statement,
 };
 
+use ui::px;
 use util::ResultExt;
 use uuid::Uuid;
 
@@ -77,10 +78,10 @@ impl Bind for SerializedWindowBounds {
                 let next_index = statement.bind(&"Windowed", start_index)?;
                 statement.bind(
                     &(
-                        SerializedDevicePixels(bounds.origin.x),
-                        SerializedDevicePixels(bounds.origin.y),
-                        SerializedDevicePixels(bounds.size.width),
-                        SerializedDevicePixels(bounds.size.height),
+                        SerializedPixels(bounds.origin.x),
+                        SerializedPixels(bounds.origin.y),
+                        SerializedPixels(bounds.size.width),
+                        SerializedPixels(bounds.size.height),
                     ),
                     next_index,
                 )
@@ -89,10 +90,10 @@ impl Bind for SerializedWindowBounds {
                 let next_index = statement.bind(&"Maximized", start_index)?;
                 statement.bind(
                     &(
-                        SerializedDevicePixels(bounds.origin.x),
-                        SerializedDevicePixels(bounds.origin.y),
-                        SerializedDevicePixels(bounds.size.width),
-                        SerializedDevicePixels(bounds.size.height),
+                        SerializedPixels(bounds.origin.x),
+                        SerializedPixels(bounds.origin.y),
+                        SerializedPixels(bounds.size.width),
+                        SerializedPixels(bounds.size.height),
                     ),
                     next_index,
                 )
@@ -101,10 +102,10 @@ impl Bind for SerializedWindowBounds {
                 let next_index = statement.bind(&"FullScreen", start_index)?;
                 statement.bind(
                     &(
-                        SerializedDevicePixels(bounds.origin.x),
-                        SerializedDevicePixels(bounds.origin.y),
-                        SerializedDevicePixels(bounds.size.width),
-                        SerializedDevicePixels(bounds.size.height),
+                        SerializedPixels(bounds.origin.x),
+                        SerializedPixels(bounds.origin.y),
+                        SerializedPixels(bounds.size.width),
+                        SerializedPixels(bounds.size.height),
                     ),
                     next_index,
                 )
@@ -116,40 +117,17 @@ impl Bind for SerializedWindowBounds {
 impl Column for SerializedWindowBounds {
     fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
         let (window_state, next_index) = String::column(statement, start_index)?;
+        let ((x, y, width, height), _): ((i32, i32, i32, i32), _) =
+            Column::column(statement, next_index)?;
+        let bounds = Bounds {
+            origin: point(px(x as f32), px(y as f32)),
+            size: size(px(width as f32), px(height as f32)),
+        };
+
         let status = match window_state.as_str() {
-            "Windowed" | "Fixed" => {
-                let ((x, y, width, height), _) = Column::column(statement, next_index)?;
-                let x: i32 = x;
-                let y: i32 = y;
-                let width: i32 = width;
-                let height: i32 = height;
-                SerializedWindowBounds(WindowBounds::Windowed(Bounds {
-                    origin: point(x.into(), y.into()),
-                    size: size(width.into(), height.into()),
-                }))
-            }
-            "Maximized" => {
-                let ((x, y, width, height), _) = Column::column(statement, next_index)?;
-                let x: i32 = x;
-                let y: i32 = y;
-                let width: i32 = width;
-                let height: i32 = height;
-                SerializedWindowBounds(WindowBounds::Maximized(Bounds {
-                    origin: point(x.into(), y.into()),
-                    size: size(width.into(), height.into()),
-                }))
-            }
-            "FullScreen" => {
-                let ((x, y, width, height), _) = Column::column(statement, next_index)?;
-                let x: i32 = x;
-                let y: i32 = y;
-                let width: i32 = width;
-                let height: i32 = height;
-                SerializedWindowBounds(WindowBounds::Fullscreen(Bounds {
-                    origin: point(x.into(), y.into()),
-                    size: size(width.into(), height.into()),
-                }))
-            }
+            "Windowed" | "Fixed" => SerializedWindowBounds(WindowBounds::Windowed(bounds)),
+            "Maximized" => SerializedWindowBounds(WindowBounds::Maximized(bounds)),
+            "FullScreen" => SerializedWindowBounds(WindowBounds::Fullscreen(bounds)),
             _ => bail!("Window State did not have a valid string"),
         };
 
@@ -158,16 +136,16 @@ impl Column for SerializedWindowBounds {
 }
 
 #[derive(Clone, Debug, PartialEq)]
-struct SerializedDevicePixels(gpui::DevicePixels);
-impl sqlez::bindable::StaticColumnCount for SerializedDevicePixels {}
+struct SerializedPixels(gpui::Pixels);
+impl sqlez::bindable::StaticColumnCount for SerializedPixels {}
 
-impl sqlez::bindable::Bind for SerializedDevicePixels {
+impl sqlez::bindable::Bind for SerializedPixels {
     fn bind(
         &self,
         statement: &sqlez::statement::Statement,
         start_index: i32,
     ) -> anyhow::Result<i32> {
-        let this: i32 = self.0.into();
+        let this: i32 = self.0 .0 as i32;
         this.bind(statement, start_index)
     }
 }

crates/workspace/src/workspace.rs 🔗

@@ -28,10 +28,10 @@ use futures::{
 };
 use gpui::{
     actions, canvas, impl_actions, point, relative, size, Action, AnyElement, AnyView, AnyWeakView,
-    AppContext, AsyncAppContext, AsyncWindowContext, Bounds, DevicePixels, DragMoveEvent,
-    Entity as _, EntityId, EventEmitter, FocusHandle, FocusableView, Global, KeyContext, Keystroke,
-    ManagedView, Model, ModelContext, PathPromptOptions, Point, PromptLevel, Render, Size,
-    Subscription, Task, View, WeakView, WindowBounds, WindowHandle, WindowOptions,
+    AppContext, AsyncAppContext, AsyncWindowContext, Bounds, DragMoveEvent, Entity as _, EntityId,
+    EventEmitter, FocusHandle, FocusableView, Global, KeyContext, Keystroke, ManagedView, Model,
+    ModelContext, PathPromptOptions, Point, PromptLevel, Render, Size, Subscription, Task, View,
+    WeakView, WindowBounds, WindowHandle, WindowOptions,
 };
 use item::{
     FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, PreviewTabsSettings,
@@ -79,7 +79,7 @@ use theme::{ActiveTheme, SystemAppearance, ThemeSettings};
 pub use toolbar::{Toolbar, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
 pub use ui;
 use ui::{
-    div, h_flex, Context as _, Div, FluentBuilder, InteractiveElement as _, IntoElement,
+    div, h_flex, px, Context as _, Div, FluentBuilder, InteractiveElement as _, IntoElement,
     ParentElement as _, Pixels, SharedString, Styled as _, ViewContext, VisualContext as _,
     WindowContext,
 };
@@ -96,11 +96,11 @@ use crate::persistence::{
 use crate::{notifications::NotificationId, persistence::model::LocalPathsOrder};
 
 lazy_static! {
-    static ref ZED_WINDOW_SIZE: Option<Size<DevicePixels>> = env::var("ZED_WINDOW_SIZE")
+    static ref ZED_WINDOW_SIZE: Option<Size<Pixels>> = env::var("ZED_WINDOW_SIZE")
         .ok()
         .as_deref()
         .and_then(parse_pixel_size_env_var);
-    static ref ZED_WINDOW_POSITION: Option<Point<DevicePixels>> = env::var("ZED_WINDOW_POSITION")
+    static ref ZED_WINDOW_POSITION: Option<Point<Pixels>> = env::var("ZED_WINDOW_POSITION")
         .ok()
         .as_deref()
         .and_then(parse_pixel_position_env_var);
@@ -3981,7 +3981,7 @@ impl Workspace {
     }
 }
 
-fn window_bounds_env_override() -> Option<Bounds<DevicePixels>> {
+fn window_bounds_env_override() -> Option<Bounds<Pixels>> {
     ZED_WINDOW_POSITION
         .zip(*ZED_WINDOW_SIZE)
         .map(|(position, size)| Bounds {
@@ -5158,18 +5158,18 @@ pub fn reload(reload: &Reload, cx: &mut AppContext) {
     .detach_and_log_err(cx);
 }
 
-fn parse_pixel_position_env_var(value: &str) -> Option<Point<DevicePixels>> {
+fn parse_pixel_position_env_var(value: &str) -> Option<Point<Pixels>> {
     let mut parts = value.split(',');
     let x: usize = parts.next()?.parse().ok()?;
     let y: usize = parts.next()?.parse().ok()?;
-    Some(point((x as i32).into(), (y as i32).into()))
+    Some(point(px(x as f32), px(y as f32)))
 }
 
-fn parse_pixel_size_env_var(value: &str) -> Option<Size<DevicePixels>> {
+fn parse_pixel_size_env_var(value: &str) -> Option<Size<Pixels>> {
     let mut parts = value.split(',');
     let width: usize = parts.next()?.parse().ok()?;
     let height: usize = parts.next()?.parse().ok()?;
-    Some(size((width as i32).into(), (height as i32).into()))
+    Some(size(px(width as f32), px(height as f32)))
 }
 
 #[cfg(test)]