From 76f6ad8ee9c498dbab3e59cba75fb0861dda9901 Mon Sep 17 00:00:00 2001 From: iamrazo Date: Sun, 23 Aug 2026 21:21:38 +0800 Subject: [PATCH 1/2] feat(window): add WindowConfig::app_id() Allow applications to declare their desktop identity (Wayland app_id / X11 WM_CLASS class and instance). Desktop environments use it to associate windows with the application's .desktop entry: dock/taskbar icons, app grouping and launch tracking. When unset, behaviour is unchanged: no set_app_id() is sent on Wayland, and X11 falls back to deriving WM_CLASS from the executable name. --- src/app_handle.rs | 18 ++++++++++++++++++ src/window.rs | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/app_handle.rs b/src/app_handle.rs index 8841ea89c..fec18ba14 100644 --- a/src/app_handle.rs +++ b/src/app_handle.rs @@ -321,6 +321,7 @@ impl ApplicationHandle { mac_os_config, web_config, font_embolden, + app_id, }: WindowConfig, ) { let logical_size = size.map(|size| LogicalSize::new(size.width, size.height)); @@ -338,6 +339,23 @@ impl ApplicationHandle { .with_resizable(resizable) .with_enabled_buttons(enabled_buttons); + // Set the application name so desktop environments can associate the + // window with its .desktop entry (dock icon, window grouping, etc). + // Without this, winit never calls set_app_id() on Wayland and windows + // have no identity at all. + #[cfg(target_os = "linux")] + if let Some(app_id) = app_id.as_deref() { + use winit::platform::x11::WindowAttributesExtX11; + window_attributes = WindowAttributesExtX11::with_name(window_attributes, app_id, app_id); + } + #[cfg(target_os = "linux")] + if let Some(app_id) = app_id.as_deref() { + use winit::platform::wayland::WindowAttributesExtWayland; + window_attributes = + WindowAttributesExtWayland::with_name(window_attributes, app_id, app_id); + } + } + #[cfg(target_arch = "wasm32")] { use wgpu::web_sys::wasm_bindgen::JsCast; diff --git a/src/window.rs b/src/window.rs index e72037480..8570305c8 100644 --- a/src/window.rs +++ b/src/window.rs @@ -40,6 +40,11 @@ pub struct WindowConfig { #[allow(dead_code)] pub(crate) mac_os_config: Option, pub(crate) web_config: Option, + /// Application identity for the desktop environment (Wayland `app_id` / + /// X11 `WM_CLASS`). Should match the application's `.desktop` file id so + /// docks/taskbars can associate windows with the app. + #[allow(dead_code)] + pub(crate) app_id: Option, } impl Default for WindowConfig { @@ -66,6 +71,7 @@ impl Default for WindowConfig { font_embolden: if cfg!(target_os = "macos") { 0.2 } else { 0. }, mac_os_config: None, web_config: None, + app_id: None, } } } @@ -166,6 +172,19 @@ impl WindowConfig { self } + /// Sets the application identity used by desktop environments to + /// associate this window with its `.desktop` entry (Wayland `app_id`, + /// X11 `WM_CLASS`). + /// + /// It should match the id of the application's `.desktop` file, e.g. + /// `org.gnome.Designer`. When unset, no app_id is requested and X11 + /// falls back to the binary name. + #[inline] + pub fn app_id(mut self, app_id: impl Into) -> Self { + self.app_id = Some(app_id.into()); + self + } + /// Sets the enabled window buttons. /// /// The default is `WindowButtons::all()`. From b0a2f3e542585ee5c23ebbc798c6132663e7065e Mon Sep 17 00:00:00 2001 From: iamrazo Date: Sun, 23 Aug 2026 21:22:22 +0800 Subject: [PATCH 2/2] feat(window): consume launcher activation token If the process was spawned by a desktop launcher, XDG_ACTIVATION_TOKEN (Wayland) or DESKTOP_STARTUP_ID (X11) identifies the pending launch created for it. Passing that token to the window makes the compositor complete the startup sequence as soon as the first surface maps. Without this, shells keep applications launched from an app grid in a "starting" state until a timeout expires (~15s in GNOME), delaying the dock/taskbar entry and proper focus. --- src/app_handle.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/app_handle.rs b/src/app_handle.rs index fec18ba14..dac1cffad 100644 --- a/src/app_handle.rs +++ b/src/app_handle.rs @@ -342,7 +342,7 @@ impl ApplicationHandle { // Set the application name so desktop environments can associate the // window with its .desktop entry (dock icon, window grouping, etc). // Without this, winit never calls set_app_id() on Wayland and windows - // have no identity at all. + // have no identity at all (see lapce#2199). #[cfg(target_os = "linux")] if let Some(app_id) = app_id.as_deref() { use winit::platform::x11::WindowAttributesExtX11; @@ -354,6 +354,16 @@ impl ApplicationHandle { window_attributes = WindowAttributesExtWayland::with_name(window_attributes, app_id, app_id); } + #[cfg(target_os = "linux")] + { + // Consume the launcher's activation/startup token so the desktop + // shell immediately associates the new window with this app + // (dock icon, focus) instead of waiting for its timeout. + use winit::platform::startup_notify::EventLoopExtStartupNotify; + if let Some(token) = event_loop.read_token_from_env() { + use winit::platform::startup_notify::WindowAttributesExtStartupNotify; + window_attributes = window_attributes.with_activation_token(token); + } } #[cfg(target_arch = "wasm32")]