Fix handling of ZED_WINDOW_{SIZE,POSITION} env vars

Max Brunsfeld and Nathan Sobo created

Co-authored-by: Nathan Sobo <nathan@zed.dev>

Change summary

crates/workspace/src/workspace.rs | 85 ++++++++++++++++++++++++--------
crates/zed/src/zed.rs             | 33 ------------
script/start-local-collaboration  |  2 
3 files changed, 66 insertions(+), 54 deletions(-)

Detailed changes

crates/workspace/src/workspace.rs 🔗

@@ -34,7 +34,10 @@ use futures::{
 use gpui::{
     actions,
     elements::*,
-    geometry::vector::Vector2F,
+    geometry::{
+        rect::RectF,
+        vector::{vec2f, Vector2F},
+    },
     impl_actions, impl_internal_actions,
     keymap_matcher::KeymapContext,
     platform::{CursorStyle, WindowOptions},
@@ -47,7 +50,7 @@ use language::LanguageRegistry;
 use std::{
     any::TypeId,
     borrow::Cow,
-    cmp,
+    cmp, env,
     future::Future,
     path::{Path, PathBuf},
     sync::Arc,
@@ -58,6 +61,7 @@ use crate::{
     notifications::simple_message_notification::{MessageNotification, OsOpen},
     persistence::model::{SerializedPane, SerializedPaneGroup, SerializedWorkspace},
 };
+use lazy_static::lazy_static;
 use log::{error, warn};
 use notifications::NotificationHandle;
 pub use pane::*;
@@ -79,6 +83,17 @@ use theme::{Theme, ThemeRegistry};
 pub use toolbar::{ToolbarItemLocation, ToolbarItemView};
 use util::ResultExt;
 
+lazy_static! {
+    static ref ZED_WINDOW_SIZE: Option<Vector2F> = env::var("ZED_WINDOW_SIZE")
+        .ok()
+        .as_deref()
+        .and_then(parse_pixel_position_env_var);
+    static ref ZED_WINDOW_POSITION: Option<Vector2F> = env::var("ZED_WINDOW_POSITION")
+        .ok()
+        .as_deref()
+        .and_then(parse_pixel_position_env_var);
+}
+
 #[derive(Clone, PartialEq)]
 pub struct RemoveWorktreeFromProject(pub WorktreeId);
 
@@ -683,28 +698,47 @@ impl Workspace {
                 DB.next_id().await.unwrap_or(0)
             };
 
-            let (bounds, display) = serialized_workspace
-                .as_ref()
-                .and_then(|sw| sw.bounds.zip(sw.display))
-                .and_then(|(mut bounds, display)| {
-                    // Stored bounds are relative to the containing display. So convert back to global coordinates if that screen still exists
-                    if let WindowBounds::Fixed(mut window_bounds) = bounds {
-                        if let Some(screen) = cx.platform().screen_by_id(display) {
-                            let screen_bounds = screen.bounds();
-                            window_bounds
-                                .set_origin_x(window_bounds.origin_x() + screen_bounds.origin_x());
-                            window_bounds
-                                .set_origin_y(window_bounds.origin_y() + screen_bounds.origin_y());
-                            bounds = WindowBounds::Fixed(window_bounds);
-                        } else {
-                            // Screen no longer exists. Return none here.
-                            return None;
+            let window_bounds_override =
+                ZED_WINDOW_POSITION
+                    .zip(*ZED_WINDOW_SIZE)
+                    .map(|(position, size)| {
+                        WindowBounds::Fixed(RectF::new(
+                            cx.platform().screens()[0].bounds().origin() + position,
+                            size,
+                        ))
+                    });
+
+            let (bounds, display) = if let Some(bounds) = window_bounds_override {
+                (Some(bounds), None)
+            } else {
+                serialized_workspace
+                    .as_ref()
+                    .and_then(|serialized_workspace| {
+                        let display = serialized_workspace.display?;
+                        let mut bounds = serialized_workspace.bounds?;
+
+                        // Stored bounds are relative to the containing display.
+                        // So convert back to global coordinates if that screen still exists
+                        if let WindowBounds::Fixed(mut window_bounds) = bounds {
+                            if let Some(screen) = cx.platform().screen_by_id(display) {
+                                let screen_bounds = screen.bounds();
+                                window_bounds.set_origin_x(
+                                    window_bounds.origin_x() + screen_bounds.origin_x(),
+                                );
+                                window_bounds.set_origin_y(
+                                    window_bounds.origin_y() + screen_bounds.origin_y(),
+                                );
+                                bounds = WindowBounds::Fixed(window_bounds);
+                            } else {
+                                // Screen no longer exists. Return none here.
+                                return None;
+                            }
                         }
-                    }
 
-                    Some((bounds, display))
-                })
-                .unzip();
+                        Some((bounds, display))
+                    })
+                    .unzip()
+            };
 
             // Use the serialized workspace to construct the new window
             let (_, workspace) = cx.add_window(
@@ -2831,6 +2865,13 @@ pub fn open_new(app_state: &Arc<AppState>, cx: &mut MutableAppContext) -> Task<(
     })
 }
 
+fn parse_pixel_position_env_var(value: &str) -> Option<Vector2F> {
+    let mut parts = value.split(',');
+    let width: usize = parts.next()?.parse().ok()?;
+    let height: usize = parts.next()?.parse().ok()?;
+    Some(vec2f(width as f32, height as f32))
+}
+
 #[cfg(test)]
 mod tests {
     use std::{cell::RefCell, rc::Rc};

crates/zed/src/zed.rs 🔗

@@ -17,16 +17,12 @@ use feedback::{
 use futures::StreamExt;
 use gpui::{
     actions,
-    geometry::{
-        rect::RectF,
-        vector::{vec2f, Vector2F},
-    },
+    geometry::vector::vec2f,
     impl_actions,
     platform::{WindowBounds, WindowOptions},
     AssetSource, AsyncAppContext, Platform, PromptLevel, TitlebarOptions, ViewContext, WindowKind,
 };
 use language::Rope;
-use lazy_static::lazy_static;
 pub use lsp;
 pub use project;
 use project_panel::ProjectPanel;
@@ -76,17 +72,6 @@ actions!(
 
 const MIN_FONT_SIZE: f32 = 6.0;
 
-lazy_static! {
-    static ref ZED_WINDOW_SIZE: Option<Vector2F> = env::var("ZED_WINDOW_SIZE")
-        .ok()
-        .as_deref()
-        .and_then(parse_pixel_position_env_var);
-    static ref ZED_WINDOW_POSITION: Option<Vector2F> = env::var("ZED_WINDOW_POSITION")
-        .ok()
-        .as_deref()
-        .and_then(parse_pixel_position_env_var);
-}
-
 pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::MutableAppContext) {
     cx.add_action(about);
     cx.add_global_action(|_: &Hide, cx: &mut gpui::MutableAppContext| {
@@ -378,14 +363,7 @@ pub fn build_window_options(
     display: Option<Uuid>,
     platform: &dyn Platform,
 ) -> WindowOptions<'static> {
-    let bounds = bounds
-        .or_else(|| {
-            ZED_WINDOW_POSITION
-                .zip(*ZED_WINDOW_SIZE)
-                .map(|(position, size)| WindowBounds::Fixed(RectF::new(position, size)))
-        })
-        .unwrap_or(WindowBounds::Maximized);
-
+    let bounds = bounds.unwrap_or(WindowBounds::Maximized);
     let screen = display.and_then(|display| platform.screen_by_id(display));
 
     WindowOptions {
@@ -683,13 +661,6 @@ fn schema_file_match(path: &Path) -> &Path {
         .unwrap()
 }
 
-fn parse_pixel_position_env_var(value: &str) -> Option<Vector2F> {
-    let mut parts = value.split(',');
-    let width: usize = parts.next()?.parse().ok()?;
-    let height: usize = parts.next()?.parse().ok()?;
-    Some(vec2f(width as f32, height as f32))
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;

script/start-local-collaboration 🔗

@@ -31,7 +31,7 @@ scale_factor=1
 if [[ $resolution_line =~ Retina ]]; then scale_factor=2; fi
 width=$(expr ${screen_size[0]} / 2 / $scale_factor)
 height=${screen_size[1] / $scale_factor}
-y=$(expr $height / 2)
+y=0
 
 position_1=0,${y}
 position_2=${width},${y}