diff --git a/src/backend/wayland/compositor/handlers.rs b/src/backend/wayland/compositor/handlers.rs index fe1fc74c..97a161e5 100644 --- a/src/backend/wayland/compositor/handlers.rs +++ b/src/backend/wayland/compositor/handlers.rs @@ -81,9 +81,25 @@ impl CompositorHandler for WaylandState { state.buffer().is_some() }) .unwrap_or(false); + + let (w, h) = smithay::backend::renderer::utils::with_renderer_surface_state(surface, |state| { + state.surface_size().map(|s| (s.w, s.h)).unwrap_or((0, 0)) + }).unwrap_or((0, 0)); + if has_buffer { let toplevel = self.pending_toplevels.swap_remove(pos); - let _ = self.map_new_toplevel(toplevel); + let win = self.map_new_toplevel(toplevel); + + // If the requested dimensions are 1x1 or 0x0, it's likely a dummy + // window (e.g. some clipboard tools use this to gain focus). + // Force it to float so it doesn't cause a layout shift. + if w <= 1 && h <= 1 { + if let Some(g) = self.globals_mut() { + if let Some(client) = g.clients.get_mut(&win) { + client.is_floating = true; + } + } + } } // No else: the surface stays in pending_toplevels until it // either commits a buffer or is destroyed. diff --git a/src/client/rules.rs b/src/client/rules.rs index 6ec18122..1b7376c4 100644 --- a/src/client/rules.rs +++ b/src/client/rules.rs @@ -32,6 +32,11 @@ pub fn apply_rules(g: &mut Globals, win: WindowId, props: &WindowProperties) { if !props.title.is_empty() { c.name = props.title.clone(); } + // wl-clipboard (wl-copy) creates dummy windows just to get keyboard focus. + // Float them immediately so they don't cause layout shifts. + if props.class == "wl-clipboard" || props.title == "wl-clipboard" { + c.is_floating = true; + } } let special_next = g.behavior.specialnext;