From 64eec67a8193dcd470d39944d4001966cb0b9467 Mon Sep 17 00:00:00 2001 From: Alvaro Parker <64918109+AlvaroParker@users.noreply.github.com> Date: Tue, 7 Oct 2025 09:06:48 -0300 Subject: [PATCH] Fix floating file chooser (#39154) Closes #39117 Some window managers (example: hyprland https://github.com/hyprwm/Hyprland/issues/11229) still won't open a floating file chooser because they don't support the XDG foreign protocol yet: https://wayland.app/protocols/xdg-foreign-unstable-v2 Release Notes: - Fixed file chooser not floating --------- Co-authored-by: David Kleingeld --- Cargo.lock | 23 +++++++++++++++++-- crates/gpui/Cargo.toml | 2 +- crates/gpui/src/platform/linux/platform.rs | 15 ++++++++++++ .../gpui/src/platform/linux/wayland/client.rs | 15 ++++++++++++ crates/gpui/src/platform/linux/x11/client.rs | 11 +++++++++ crates/gpui/src/platform/linux/x11/window.rs | 2 +- tooling/workspace-hack/Cargo.toml | 21 +++++++++++++++++ 7 files changed, 85 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f4f5a49232218d54b9a24f712129c673610fe982..2b63385619d0c7930f77d3c5a94cd9ec32c1a96b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -778,6 +778,9 @@ dependencies = [ "serde", "serde_repr", "url", + "wayland-backend", + "wayland-client", + "wayland-protocols 0.32.6", "zbus", ] @@ -7062,7 +7065,7 @@ dependencies = [ "wayland-backend", "wayland-client", "wayland-cursor", - "wayland-protocols", + "wayland-protocols 0.31.2", "wayland-protocols-plasma", "windows 0.61.1", "windows-core 0.61.0", @@ -18167,6 +18170,18 @@ dependencies = [ "wayland-scanner", ] +[[package]] +name = "wayland-protocols" +version = "0.32.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0781cf46869b37e36928f7b432273c0995aa8aed9552c556fb18754420541efc" +dependencies = [ + "bitflags 2.9.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + [[package]] name = "wayland-protocols-plasma" version = "0.2.0" @@ -18176,7 +18191,7 @@ dependencies = [ "bitflags 2.9.0", "wayland-backend", "wayland-client", - "wayland-protocols", + "wayland-protocols 0.31.2", "wayland-scanner", ] @@ -19425,6 +19440,7 @@ dependencies = [ "aho-corasick", "anstream", "arrayvec", + "ashpd 0.11.0", "async-compression", "async-std", "async-tungstenite", @@ -19597,6 +19613,8 @@ dependencies = [ "wasmtime", "wasmtime-cranelift", "wasmtime-environ", + "wayland-backend", + "wayland-sys", "winapi", "windows-core 0.61.0", "windows-numerics", @@ -19604,6 +19622,7 @@ dependencies = [ "windows-sys 0.52.0", "windows-sys 0.59.0", "windows-sys 0.61.0", + "zbus_macros", "zeroize", "zvariant", ] diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 761803fe7f38f11ad24e82e718c331a237f4609d..39cadeb1d45846cda43c27a0a51a1f07b27c0978 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -43,7 +43,7 @@ wayland = [ "blade-macros", "blade-util", "bytemuck", - "ashpd", + "ashpd/wayland", "cosmic-text", "font-kit", "calloop-wayland-source", diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 14405923ed5a78ac94f9c9a1a10fa7662a7b9649..322f5d76110ee36e3cfdf26449bbec85c3d51af5 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -73,6 +73,13 @@ pub trait LinuxClient { fn active_window(&self) -> Option; fn window_stack(&self) -> Option>; fn run(&self); + + #[cfg(any(feature = "wayland", feature = "x11"))] + fn window_identifier( + &self, + ) -> impl Future> + Send + 'static { + std::future::ready::>(None) + } } #[derive(Default)] @@ -290,6 +297,9 @@ impl Platform for P { #[cfg(not(any(feature = "wayland", feature = "x11")))] let _ = (done_tx.send(Ok(None)), options); + #[cfg(any(feature = "wayland", feature = "x11"))] + let identifier = self.window_identifier(); + #[cfg(any(feature = "wayland", feature = "x11"))] self.foreground_executor() .spawn(async move { @@ -300,6 +310,7 @@ impl Platform for P { }; let request = match ashpd::desktop::file_chooser::OpenFileRequest::default() + .identifier(identifier.await) .modal(true) .title(title) .accept_label(options.prompt.as_ref().map(crate::SharedString::as_str)) @@ -346,6 +357,9 @@ impl Platform for P { #[cfg(not(any(feature = "wayland", feature = "x11")))] let _ = (done_tx.send(Ok(None)), directory, suggested_name); + #[cfg(any(feature = "wayland", feature = "x11"))] + let identifier = self.window_identifier(); + #[cfg(any(feature = "wayland", feature = "x11"))] self.foreground_executor() .spawn({ @@ -355,6 +369,7 @@ impl Platform for P { async move { let mut request_builder = ashpd::desktop::file_chooser::SaveFileRequest::default() + .identifier(identifier.await) .modal(true) .title("Save File") .current_folder(directory) diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index 8596bddc8dd821426982d618f661d6da621096bb..f8672971ec51415ba93708f2b06be49678aa3738 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -7,6 +7,7 @@ use std::{ time::{Duration, Instant}, }; +use ashpd::WindowIdentifier; use calloop::{ EventLoop, LoopHandle, timer::{TimeoutAction, Timer}, @@ -858,6 +859,20 @@ impl LinuxClient for WaylandClient { fn compositor_name(&self) -> &'static str { "Wayland" } + + fn window_identifier(&self) -> impl Future> + Send + 'static { + async fn inner(surface: Option) -> Option { + if let Some(surface) = surface { + ashpd::WindowIdentifier::from_wayland(&surface).await + } else { + None + } + } + + let client_state = self.0.borrow(); + let active_window = client_state.keyboard_focused_window.as_ref(); + inner(active_window.map(|aw| aw.surface())) + } } impl Dispatch for WaylandClientStatePtr { diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs index 481f99b0c1de22d14a9f8e8b4eedac36b6fdd692..497e3ff709e8cd2f853d50dc13e4f96f907e4008 100644 --- a/crates/gpui/src/platform/linux/x11/client.rs +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -1,5 +1,6 @@ use crate::{Capslock, xcb_flush}; use anyhow::{Context as _, anyhow}; +use ashpd::WindowIdentifier; use calloop::{ EventLoop, LoopHandle, RegistrationToken, generic::{FdWrapper, Generic}, @@ -1652,6 +1653,16 @@ impl LinuxClient for X11Client { Some(handles) } + + fn window_identifier(&self) -> impl Future> + Send + 'static { + let state = self.0.borrow(); + state + .keyboard_focused_window + .and_then(|focused_window| state.windows.get(&focused_window)) + .map(|window| window.window.x_window as u64) + .map(|x_window| std::future::ready(Some(WindowIdentifier::from_xid(x_window)))) + .unwrap_or(std::future::ready(None)) + } } impl X11ClientState { diff --git a/crates/gpui/src/platform/linux/x11/window.rs b/crates/gpui/src/platform/linux/x11/window.rs index b189d448395c75f2b22af333446289354bcbd4cf..001b853afee1d1a2164f330f0bfdf81b77d8fc02 100644 --- a/crates/gpui/src/platform/linux/x11/window.rs +++ b/crates/gpui/src/platform/linux/x11/window.rs @@ -284,7 +284,7 @@ pub(crate) struct X11WindowStatePtr { pub state: Rc>, pub(crate) callbacks: Rc>, xcb: Rc, - x_window: xproto::Window, + pub(crate) x_window: xproto::Window, } impl rwh::HasWindowHandle for RawWindow { diff --git a/tooling/workspace-hack/Cargo.toml b/tooling/workspace-hack/Cargo.toml index b2ce439f30680abdd6817391183a5a8caa34958e..6434f69723d7c525a0fdbae9cb71d3073874c913 100644 --- a/tooling/workspace-hack/Cargo.toml +++ b/tooling/workspace-hack/Cargo.toml @@ -408,6 +408,7 @@ tower = { version = "0.5", default-features = false, features = ["timeout", "uti [target.x86_64-unknown-linux-gnu.dependencies] aes = { version = "0.8", default-features = false, features = ["zeroize"] } ahash = { version = "0.8", default-features = false, features = ["compile-time-rng"] } +ashpd = { version = "0.11", default-features = false, features = ["async-std", "wayland"] } bytemuck = { version = "1", default-features = false, features = ["min_const_generics"] } cipher = { version = "0.4", default-features = false, features = ["block-padding", "rand_core", "zeroize"] } codespan-reporting = { version = "0.12" } @@ -446,12 +447,15 @@ tokio-rustls = { version = "0.26", default-features = false, features = ["loggin tokio-socks = { version = "0.5", features = ["futures-io"] } tokio-stream = { version = "0.1", features = ["fs"] } tower = { version = "0.5", default-features = false, features = ["timeout", "util"] } +wayland-backend = { version = "0.3", default-features = false, features = ["client_system", "dlopen"] } +wayland-sys = { version = "0.31", default-features = false, features = ["client", "dlopen"] } zeroize = { version = "1", features = ["zeroize_derive"] } zvariant = { version = "5", features = ["enumflags2", "gvariant", "url"] } [target.x86_64-unknown-linux-gnu.build-dependencies] aes = { version = "0.8", default-features = false, features = ["zeroize"] } ahash = { version = "0.8", default-features = false, features = ["compile-time-rng"] } +ashpd = { version = "0.11", default-features = false, features = ["async-std", "wayland"] } bytemuck = { version = "1", default-features = false, features = ["min_const_generics"] } cipher = { version = "0.4", default-features = false, features = ["block-padding", "rand_core", "zeroize"] } codespan-reporting = { version = "0.12" } @@ -488,12 +492,16 @@ tokio-rustls = { version = "0.26", default-features = false, features = ["loggin tokio-socks = { version = "0.5", features = ["futures-io"] } tokio-stream = { version = "0.1", features = ["fs"] } tower = { version = "0.5", default-features = false, features = ["timeout", "util"] } +wayland-backend = { version = "0.3", default-features = false, features = ["client_system", "dlopen"] } +wayland-sys = { version = "0.31", default-features = false, features = ["client", "dlopen"] } +zbus_macros = { version = "5", features = ["gvariant"] } zeroize = { version = "1", features = ["zeroize_derive"] } zvariant = { version = "5", features = ["enumflags2", "gvariant", "url"] } [target.aarch64-unknown-linux-gnu.dependencies] aes = { version = "0.8", default-features = false, features = ["zeroize"] } ahash = { version = "0.8", default-features = false, features = ["compile-time-rng"] } +ashpd = { version = "0.11", default-features = false, features = ["async-std", "wayland"] } bytemuck = { version = "1", default-features = false, features = ["min_const_generics"] } cipher = { version = "0.4", default-features = false, features = ["block-padding", "rand_core", "zeroize"] } codespan-reporting = { version = "0.12" } @@ -532,12 +540,15 @@ tokio-rustls = { version = "0.26", default-features = false, features = ["loggin tokio-socks = { version = "0.5", features = ["futures-io"] } tokio-stream = { version = "0.1", features = ["fs"] } tower = { version = "0.5", default-features = false, features = ["timeout", "util"] } +wayland-backend = { version = "0.3", default-features = false, features = ["client_system", "dlopen"] } +wayland-sys = { version = "0.31", default-features = false, features = ["client", "dlopen"] } zeroize = { version = "1", features = ["zeroize_derive"] } zvariant = { version = "5", features = ["enumflags2", "gvariant", "url"] } [target.aarch64-unknown-linux-gnu.build-dependencies] aes = { version = "0.8", default-features = false, features = ["zeroize"] } ahash = { version = "0.8", default-features = false, features = ["compile-time-rng"] } +ashpd = { version = "0.11", default-features = false, features = ["async-std", "wayland"] } bytemuck = { version = "1", default-features = false, features = ["min_const_generics"] } cipher = { version = "0.4", default-features = false, features = ["block-padding", "rand_core", "zeroize"] } codespan-reporting = { version = "0.12" } @@ -574,6 +585,9 @@ tokio-rustls = { version = "0.26", default-features = false, features = ["loggin tokio-socks = { version = "0.5", features = ["futures-io"] } tokio-stream = { version = "0.1", features = ["fs"] } tower = { version = "0.5", default-features = false, features = ["timeout", "util"] } +wayland-backend = { version = "0.3", default-features = false, features = ["client_system", "dlopen"] } +wayland-sys = { version = "0.31", default-features = false, features = ["client", "dlopen"] } +zbus_macros = { version = "5", features = ["gvariant"] } zeroize = { version = "1", features = ["zeroize_derive"] } zvariant = { version = "5", features = ["enumflags2", "gvariant", "url"] } @@ -631,6 +645,7 @@ windows-sys-d4189bed749088b6 = { package = "windows-sys", version = "0.61", feat [target.x86_64-unknown-linux-musl.dependencies] aes = { version = "0.8", default-features = false, features = ["zeroize"] } ahash = { version = "0.8", default-features = false, features = ["compile-time-rng"] } +ashpd = { version = "0.11", default-features = false, features = ["async-std", "wayland"] } bytemuck = { version = "1", default-features = false, features = ["min_const_generics"] } cipher = { version = "0.4", default-features = false, features = ["block-padding", "rand_core", "zeroize"] } codespan-reporting = { version = "0.12" } @@ -669,12 +684,15 @@ tokio-rustls = { version = "0.26", default-features = false, features = ["loggin tokio-socks = { version = "0.5", features = ["futures-io"] } tokio-stream = { version = "0.1", features = ["fs"] } tower = { version = "0.5", default-features = false, features = ["timeout", "util"] } +wayland-backend = { version = "0.3", default-features = false, features = ["client_system", "dlopen"] } +wayland-sys = { version = "0.31", default-features = false, features = ["client", "dlopen"] } zeroize = { version = "1", features = ["zeroize_derive"] } zvariant = { version = "5", features = ["enumflags2", "gvariant", "url"] } [target.x86_64-unknown-linux-musl.build-dependencies] aes = { version = "0.8", default-features = false, features = ["zeroize"] } ahash = { version = "0.8", default-features = false, features = ["compile-time-rng"] } +ashpd = { version = "0.11", default-features = false, features = ["async-std", "wayland"] } bytemuck = { version = "1", default-features = false, features = ["min_const_generics"] } cipher = { version = "0.4", default-features = false, features = ["block-padding", "rand_core", "zeroize"] } codespan-reporting = { version = "0.12" } @@ -711,6 +729,9 @@ tokio-rustls = { version = "0.26", default-features = false, features = ["loggin tokio-socks = { version = "0.5", features = ["futures-io"] } tokio-stream = { version = "0.1", features = ["fs"] } tower = { version = "0.5", default-features = false, features = ["timeout", "util"] } +wayland-backend = { version = "0.3", default-features = false, features = ["client_system", "dlopen"] } +wayland-sys = { version = "0.31", default-features = false, features = ["client", "dlopen"] } +zbus_macros = { version = "5", features = ["gvariant"] } zeroize = { version = "1", features = ["zeroize_derive"] } zvariant = { version = "5", features = ["enumflags2", "gvariant", "url"] }