mirror of
https://github.com/zed-industries/zed.git
synced 2026-07-09 16:00:35 +00:00
gpui_linux: Consume Wayland startup activation token (#59995)
When opening Zed from GNOME on Wayland, the cursor can stay stuck in the launch spinner state for several seconds because GPUI never tells the compositor that the launched app's first window has loaded. Desktop launchers pass this information through `XDG_ACTIVATION_TOKEN`, which Wayland clients are expected to consume once their first toplevel surface is ready. This PR fixes that by reading and removing `XDG_ACTIVATION_TOKEN` during Wayland client startup, storing it on the client state, and consuming it on the first XDG toplevel surface via the existing `xdg_activation_v1.activate(token, surface)` plumbing. Anthony manually verified this on GNOME Wayland. docs ref: https://wayland.app/protocols/xdg-activation-v1 Release Notes: - Fixed Linux Wayland cursor being stuck as a spinner for a couple of seconds.
This commit is contained in:
parent
0a7c84bc37
commit
7c3160b7bf
1 changed files with 31 additions and 0 deletions
|
|
@ -108,6 +108,19 @@ use wayland_protocols::wp::linux_dmabuf::zv1::client::{
|
|||
const MIN_KEYCODE: u32 = 8;
|
||||
|
||||
const UNKNOWN_KEYBOARD_LAYOUT_NAME: SharedString = SharedString::new_static("unknown");
|
||||
const XDG_ACTIVATION_TOKEN_ENV_VAR: &str = "XDG_ACTIVATION_TOKEN";
|
||||
|
||||
fn take_startup_activation_token_from_environment() -> Option<String> {
|
||||
let startup_activation_token = std::env::var(XDG_ACTIVATION_TOKEN_ENV_VAR)
|
||||
.ok()
|
||||
.filter(|token| !token.is_empty());
|
||||
// The token must be removed from the environment so it isn't inherited by child
|
||||
// processes we spawn, per the xdg-activation spec: https://wayland.app/protocols/xdg-activation-v1
|
||||
// SAFETY: This runs during Wayland platform initialization before GPUI starts
|
||||
// concurrent environment access or spawning child processes.
|
||||
unsafe { std::env::remove_var(XDG_ACTIVATION_TOKEN_ENV_VAR) };
|
||||
startup_activation_token
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Globals {
|
||||
|
|
@ -262,6 +275,7 @@ pub(crate) struct WaylandClientState {
|
|||
primary_data_offer: Option<DataOffer<ZwpPrimarySelectionOfferV1>>,
|
||||
cursor: Cursor,
|
||||
pending_activation: Option<PendingActivation>,
|
||||
startup_activation_token: Option<String>,
|
||||
event_loop: Option<EventLoop<'static, WaylandClientStatePtr>>,
|
||||
pub common: LinuxCommon,
|
||||
ime_enabled: Option<bool>,
|
||||
|
|
@ -296,6 +310,18 @@ pub(crate) enum PendingActivation {
|
|||
Window(ObjectId),
|
||||
}
|
||||
|
||||
impl WaylandClientState {
|
||||
fn consume_startup_activation_token(&mut self, surface: &wl_surface::WlSurface) {
|
||||
let Some(startup_activation_token) = self.startup_activation_token.take() else {
|
||||
return;
|
||||
};
|
||||
let Some(activation) = self.globals.activation.as_ref() else {
|
||||
return;
|
||||
};
|
||||
activation.activate(startup_activation_token, surface);
|
||||
}
|
||||
}
|
||||
|
||||
/// This struct is required to conform to Rust's orphan rules, so we can dispatch on the state but hand the
|
||||
/// window to GPUI.
|
||||
#[derive(Clone)]
|
||||
|
|
@ -537,6 +563,7 @@ fn wl_output_version(version: u32) -> u32 {
|
|||
|
||||
impl WaylandClient {
|
||||
pub(crate) fn new() -> Self {
|
||||
let startup_activation_token = take_startup_activation_token_from_environment();
|
||||
let conn = Connection::connect_to_env().unwrap();
|
||||
|
||||
let (globals, event_queue) = registry_queue_init::<WaylandClientStatePtr>(&conn).unwrap();
|
||||
|
|
@ -741,6 +768,7 @@ impl WaylandClient {
|
|||
primary_data_offer: None,
|
||||
cursor,
|
||||
pending_activation: None,
|
||||
startup_activation_token,
|
||||
event_loop: Some(event_loop),
|
||||
ime_enabled: None,
|
||||
}));
|
||||
|
|
@ -842,6 +870,9 @@ impl LinuxClient for WaylandClient {
|
|||
parent,
|
||||
target_output,
|
||||
)?;
|
||||
if window.0.toplevel().is_some() {
|
||||
state.consume_startup_activation_token(&window.0.surface());
|
||||
}
|
||||
state.windows.insert(surface_id, window.0.clone());
|
||||
|
||||
Ok(Box::new(window))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue