diff --git a/crates/gpui_linux/src/linux/x11/window.rs b/crates/gpui_linux/src/linux/x11/window.rs index 4fde66f06c..94037067e7 100644 --- a/crates/gpui_linux/src/linux/x11/window.rs +++ b/crates/gpui_linux/src/linux/x11/window.rs @@ -389,6 +389,39 @@ where .with_context(failure_context) } +/// Sets or clears the ICCCM WM_HINTS urgency flag, preserving the other hints. +/// +/// Clearing when the flag isn't set is skipped: writing it back would create a +/// WM_HINTS property on windows that never requested attention, and would add an +/// X round trip to every window state change. +fn set_wm_hints_urgency(xcb: &XCBConnection, x_window: xproto::Window, urgent: bool) { + let mut hints = WmHints::new(); + match WmHints::get(xcb, x_window) { + Ok(cookie) => match cookie.reply() { + Ok(Some(existing_hints)) => hints = existing_hints, + Ok(None) => {} + Err(error) => { + log::debug!("failed to read X11 WM_HINTS before setting urgency: {error}") + } + }, + Err(error) => { + log::debug!("failed to request X11 WM_HINTS before setting urgency: {error}") + } + } + + if !urgent && !hints.urgent { + return; + } + + hints.urgent = urgent; + check_reply( + || "X11 ChangeProperty for WM_HINTS urgency failed.", + hints.set(xcb, x_window), + ) + .log_err(); + xcb_flush(xcb); +} + /// Convert X11 connection errors to `anyhow::Error` and panic for unrecoverable errors. pub(crate) fn handle_connection_error(err: ConnectionError) -> anyhow::Error { match err { @@ -1080,6 +1113,7 @@ impl X11WindowStatePtr { .chunks_exact(4) .map(|chunk| u32::from_ne_bytes([chunk[0], chunk[1], chunk[2], chunk[3]])); + let was_active = state.active; state.active = false; state.fullscreen = false; state.maximized_vertical = false; @@ -1100,6 +1134,12 @@ impl X11WindowStatePtr { } } + // The urgency hint has no withdrawal signal of its own; ICCCM leaves that to + // the client, and focus is the conventional means for the user to zero it. + if state.active && !was_active { + set_wm_hints_urgency(&self.xcb, self.x_window, false); + } + Ok(()) } @@ -1497,26 +1537,7 @@ impl PlatformWindow for X11Window { return; } - let mut hints = WmHints::new(); - match WmHints::get(&*self.0.xcb, self.0.x_window) { - Ok(cookie) => match cookie.reply() { - Ok(Some(existing_hints)) => hints = existing_hints, - Ok(None) => {} - Err(error) => { - log::debug!("failed to read X11 WM_HINTS before setting urgency: {error}") - } - }, - Err(error) => { - log::debug!("failed to request X11 WM_HINTS before setting urgency: {error}") - } - } - hints.urgent = true; - check_reply( - || "X11 ChangeProperty for WM_HINTS urgency failed.", - hints.set(&*self.0.xcb, self.0.x_window), - ) - .log_err(); - xcb_flush(&self.0.xcb); + set_wm_hints_urgency(&self.0.xcb, self.0.x_window, true); } fn is_active(&self) -> bool {