Make notification windows not have titles on X11 (#12935)

Conrad Irwin and Max created

Co-Authored-By: Max <max@zed.dev>

Release Notes:

- N/A

Co-authored-by: Max <max@zed.dev>

Change summary

crates/gpui/src/platform/linux/x11/window.rs | 15 +++++
crates/zed/src/main.rs                       |  8 +-
script/zed-local                             | 57 +++++++++++++++------
3 files changed, 58 insertions(+), 22 deletions(-)

Detailed changes

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

@@ -3,7 +3,7 @@ use crate::{
     size, AnyWindowHandle, Bounds, DevicePixels, ForegroundExecutor, Modifiers, Pixels,
     PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point,
     PromptLevel, Scene, Size, WindowAppearance, WindowBackgroundAppearance, WindowBounds,
-    WindowParams, X11ClientStatePtr,
+    WindowKind, WindowParams, X11ClientStatePtr,
 };
 
 use blade_graphics as gpu;
@@ -47,6 +47,8 @@ x11rb::atom_manager! {
         _NET_WM_STATE_HIDDEN,
         _NET_WM_STATE_FOCUSED,
         _NET_WM_MOVERESIZE,
+        _NET_WM_WINDOW_TYPE,
+        _NET_WM_WINDOW_TYPE_NOTIFICATION,
         _GTK_SHOW_WINDOW_MENU,
     }
 }
@@ -296,6 +298,17 @@ impl X11WindowState {
                     .unwrap();
             }
         }
+        if params.kind == WindowKind::PopUp {
+            xcb_connection
+                .change_property32(
+                    xproto::PropMode::REPLACE,
+                    x_window,
+                    atoms._NET_WM_WINDOW_TYPE,
+                    xproto::AtomEnum::ATOM,
+                    &[atoms._NET_WM_WINDOW_TYPE_NOTIFICATION],
+                )
+                .unwrap();
+        }
 
         xcb_connection
             .change_property32(

crates/zed/src/main.rs 🔗

@@ -303,9 +303,11 @@ fn main() {
 
     #[cfg(target_os = "linux")]
     {
-        if crate::zed::listen_for_cli_connections(open_listener.clone()).is_err() {
-            println!("zed is already running");
-            return;
+        if env::var("ZED_STATELESS").is_err() {
+            if crate::zed::listen_for_cli_connections(open_listener.clone()).is_err() {
+                println!("zed is already running");
+                return;
+            }
         }
     }
     #[cfg(not(target_os = "linux"))]

script/zed-local 🔗

@@ -17,7 +17,7 @@ OPTIONS
   --stable         Use stable Zed release installed on local machine for all instances (except for the first one).
 `.trim();
 
-const { spawn, execFileSync } = require("child_process");
+const { spawn, execSync, execFileSync } = require("child_process");
 const assert = require("assert");
 
 let users;
@@ -68,24 +68,43 @@ while (args.length > 0) {
 
   args.shift();
 }
-
-// Parse the resolution of the main screen
-const displayInfo = JSON.parse(
-  execFileSync("system_profiler", ["SPDisplaysDataType", "-json"], {
-    encoding: "utf8",
-  }),
-);
-const mainDisplayResolution = displayInfo?.SPDisplaysDataType?.flatMap(
-  (display) => display?.spdisplays_ndrvs,
-)
-  ?.find((entry) => entry?.spdisplays_main === "spdisplays_yes")
-  ?._spdisplays_resolution?.match(RESOLUTION_REGEX);
-if (!mainDisplayResolution) {
-  throw new Error("Could not parse screen resolution");
+const os = require("os");
+const platform = os.platform();
+
+let screenWidth, screenHeight;
+
+if (platform === "darwin") {
+  // macOS
+  const displayInfo = JSON.parse(
+    execFileSync("system_profiler", ["SPDisplaysDataType", "-json"], {
+      encoding: "utf8",
+    }),
+  );
+  const mainDisplayResolution = displayInfo?.SPDisplaysDataType?.flatMap(
+    (display) => display?.spdisplays_ndrvs,
+  )
+    ?.find((entry) => entry?.spdisplays_main === "spdisplays_yes")
+    ?._spdisplays_resolution?.match(RESOLUTION_REGEX);
+  if (!mainDisplayResolution) {
+    throw new Error("Could not parse screen resolution");
+  }
+  screenWidth = parseInt(mainDisplayResolution[1]);
+  screenHeight = parseInt(mainDisplayResolution[2]) - titleBarHeight;
+} else if (platform === "linux") {
+  // Linux
+  try {
+    const xrandrOutput = execSync('xrandr | grep "\\*" | cut -d" " -f4', {
+      encoding: "utf8",
+    }).trim();
+    [screenWidth, screenHeight] = xrandrOutput.split("x").map(Number);
+  } catch (err) {
+    console.log(err);
+    throw new Error("Could not get screen resolution");
+  }
 }
+
 const titleBarHeight = 24;
-const screenWidth = parseInt(mainDisplayResolution[1]);
-let screenHeight = parseInt(mainDisplayResolution[2]) - titleBarHeight;
+screenHeight -= titleBarHeight;
 
 if (isTop) {
   screenHeight = Math.floor(screenHeight / 2);
@@ -128,7 +147,9 @@ if (isReleaseMode) {
 }
 
 try {
-  execFileSync("cargo", buildArgs, { stdio: "inherit" });
+  execFileSync("cargo", buildArgs, {
+    stdio: "inherit",
+  });
 } catch (e) {
   process.exit(0);
 }