diff --git a/src/app_handle.rs b/src/app_handle.rs index 8841ea89c..dac1cffad 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,33 @@ 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 (see lapce#2199). + #[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_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")] { 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()`.