Protect selected windows from screen capture

Add capture privacy rules for windows, transient children, overview previews and client cursors, with separate capture rendering and deferred-frame invalidation. Keep detached close snapshots out of captures after clients leave the registry.

Validated with release workspace tests, private compositor/EGL regressions, and an isolated release PipeWire capture test.
This commit is contained in:
Samuel Onoja authored and GitHub committed 2026-10-03 10:49:01 +01:00
1 parent bffd538dd4
commit 453ec16583
24 files changed
+1132 -41

No files matched your search

+21 -11
View File
@@ -18,7 +18,7 @@ use smithay::wayland::presentation::Refresh;
use crate::Ferese;
use crate::metrics::{FrameEffectMetrics, RenderMetrics};
use crate::render::{animated_window_elements, frame_effect_metrics, layer_surfaces, output_elements, redraw_output};
use crate::render::{animated_window_elements, frame_effect_metrics, layer_surfaces};
pub(crate) type NestedBackend = Rc<RefCell<winit::WinitGraphicsBackend<GlesRenderer>>>;
type DamageRenderResult = Result<
@@ -89,6 +89,7 @@ pub fn init(event_loop: &mut EventLoop<Ferese>, state: &mut Ferese) -> Result<()
TimeoutAction::ToDuration(redraw_refresh.get())
})?;
let mut capture_texture = None;
event_loop
.handle()
.insert_source(event_source, move |event, _, state| {
@@ -140,16 +141,25 @@ pub fn init(event_loop: &mut EventLoop<Ferese>, state: &mut Ferese) -> Result<()
&elements,
[0.035, 0.04, 0.055, 1.0],
)?;
let cursorless_capture = state.has_pending_screencopy(&output, false);
let captured_with_cursor =
state.process_screencopies(renderer, &framebuffer, &output, true);
if cursorless_capture {
let cursorless_elements = output_elements(state, renderer, &output, false);
redraw_output(renderer, &mut framebuffer, &output, &cursorless_elements)?;
state.process_screencopies(renderer, &framebuffer, &output, false);
redraw_output(renderer, &mut framebuffer, &output, &elements)?;
} else if captured_with_cursor {
// Keep the existing readback-only path when the
// displayed scene has no privacy exclusions.
let mut capture_changed_binding = !state.has_capture_exclusions()
&& state.process_screencopies(renderer, &framebuffer, &output, true);
if state.has_pending_screencopy(&output, false)
|| state.has_pending_screencopy(&output, true)
{
let scene = state.sample_frame(&output, Duration::ZERO);
crate::backends::direct::capture::capture_output(
state,
renderer,
&mut capture_texture,
&output,
&scene,
)?;
capture_changed_binding = true;
}
if capture_changed_binding {
// Restore the display framebuffer binding after offscreen readback.
let _ = renderer
.render(
&mut framebuffer,