diff options
Diffstat (limited to 'crates/wayland/src/winit')
| -rw-r--r-- | crates/wayland/src/winit/capture.rs | 52 | ||||
| -rw-r--r-- | crates/wayland/src/winit/render.rs | 9 |
2 files changed, 59 insertions, 2 deletions
diff --git a/crates/wayland/src/winit/capture.rs b/crates/wayland/src/winit/capture.rs index 3b1d38e..d21ed72 100644 --- a/crates/wayland/src/winit/capture.rs +++ b/crates/wayland/src/winit/capture.rs @@ -28,9 +28,34 @@ impl WaylandPlatform { // any layer-shell surface at all. let hide_top_layers = self.wm.borrow().visible_windows_front_to_back().any(|w| w.fullscreen); let mut custom_elements: Vec<crate::elements::OverlayElement<GlesRenderer>> = Vec::new(); + // Popups first, so they land above everything else - exactly the + // order both on-screen render loops already use. + // + // This pass had no popup step at all, so no tooltip, dropdown or + // right-click menu could ever appear in a screenshot taken on this + // backend, no matter how correctly it was drawn on screen. That is + // a capture-only gap, not a rendering one: the DRM backend serves + // screencopy out of its own on-screen frame (`udev/render.rs` + // drains `screencopy_pending` and hands `service_pending` the same + // framebuffer it just drew), so it never had the gap; this backend + // renders the scene a second time into an offscreen buffer, and + // that second scene was missing a tier. + // + // It cost real time to find. The whole point of the nested backend + // is validating behaviour with `grim`, and this made `grim` state + // the opposite of the truth about every popup: a menu that drew + // perfectly on screen photographed as absent, which reads exactly + // like the client never opened one. + let popup_targets = crate::elements::popup_targets(&self.state); + custom_elements.extend(crate::elements::popup_render_elements(&popup_targets, renderer, (0, 0))); if !hide_top_layers { custom_elements.extend(crate::elements::output_layer_elements(renderer, &self.output, |layer| matches!(layer, Layer::Top | Layer::Overlay))); } + // Front-to-back, so a window already pushed occludes everything a + // later one draws - accumulated for the shadow clip below, exactly + // as both on-screen render loops do it. + let monitor_bounds: Vec<srdwm_core::Rect> = self.wm.borrow().monitors().iter().map(|m| m.full_geometry).collect(); + let mut occluders: Vec<srdwm_core::Rect> = Vec::new(); for id in self.wm.borrow().visible_windows_front_to_back().map(|w| w.id).collect::<Vec<_>>() { let Some(w) = self.wm.borrow().window(id).cloned() else { continue }; if let Some(deco) = self.state.decorations.get(&id) { @@ -55,6 +80,33 @@ impl WaylandPlatform { custom_elements.extend(crate::elements::surface_content_elements(renderer, &surface, pos, w.opacity)); } } + // The drop shadow, last in this window's own group so it sits + // under its own decoration and content but still over every + // window behind it. `shadow_buffers` only holds an entry for a + // window that is meant to have one at all (see + // `state/lifecycle.rs`), so no separate floating/maximized + // check belongs here. + // + // Shadows were the other half of this pass's missing tier: a + // screenshot taken on this backend showed no shadow on any + // window, which is exactly the thing a shadow bug gets reported + // and re-checked by. Border strips are still absent - a real + // remaining gap, called out here rather than left silent. + if let Some(shadow) = self.state.shadow_buffers.get(&id) { + let full = crate::decoration::shadow_rect(w.geometry); + let rect = crate::decoration::shadow_rect_clipped(w.geometry, &monitor_bounds); + for fragment in crate::elements::visible_border_fragments(rect, &occluders) { + let src = Rectangle::new( + Point::from(((fragment.x - full.x) as f64, (fragment.y - full.y) as f64)), + Size::from((fragment.width as f64, fragment.height as f64)), + ); + match MemoryRenderBufferRenderElement::from_buffer(renderer, (fragment.x as f64, fragment.y as f64), shadow, None, Some(src), None, Kind::Unspecified) { + Ok(elem) => custom_elements.push(crate::elements::OverlayElement::Memory(elem)), + Err(e) => log::warn!("screencopy: failed to import shadow buffer for window {id}: {e}"), + } + } + } + occluders.push(w.geometry); } custom_elements.extend(crate::elements::output_layer_elements(renderer, &self.output, |layer| matches!(layer, Layer::Background | Layer::Bottom))); diff --git a/crates/wayland/src/winit/render.rs b/crates/wayland/src/winit/render.rs index a07e074..dff425f 100644 --- a/crates/wayland/src/winit/render.rs +++ b/crates/wayland/src/winit/render.rs @@ -210,6 +210,10 @@ impl WaylandPlatform { // it correctly. The border strips and titlebar bitmap are // different: outside `geometry`, so they still need `occluders`' // explicit clip against whichever window is stacked in front. + // See the matching push in `udev/render.rs` - this backend only + // ever has the one output, so the clip is a no-op here, but reading + // the same source keeps the two shadow paths from drifting. + let monitor_bounds: Vec<srdwm_core::Rect> = self.wm.borrow().monitors().iter().map(|m| m.full_geometry).collect(); let mut occluders: Vec<srdwm_core::Rect> = Vec::with_capacity(ids.len()); for id in ids { let Some(w) = self.wm.borrow().window(id).cloned() else { continue }; @@ -405,11 +409,12 @@ impl WaylandPlatform { // exactly where two windows' corners nearly meet, which this // compositor's default cascade placement does constantly. if let Some(shadow) = self.state.shadow_buffers.get(&id) { - let rect = decoration::shadow_rect(frame); + let full = decoration::shadow_rect(frame); + let rect = decoration::shadow_rect_clipped(frame, &monitor_bounds); for fragment in crate::elements::visible_border_fragments(rect, &occluders) { let pos = (fragment.x as f64, fragment.y as f64); let src = Rectangle::new( - Point::from(((fragment.x - rect.x) as f64, (fragment.y - rect.y) as f64)), + Point::from(((fragment.x - full.x) as f64, (fragment.y - full.y) as f64)), Size::from((fragment.width as f64, fragment.height as f64)), ); match MemoryRenderBufferRenderElement::from_buffer(renderer, pos, shadow, None, Some(src), None, Kind::Unspecified) { |