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

+30 -2
View File
@@ -31,6 +31,32 @@ pub(crate) fn sampled_output_elements(
output: &Output,
include_cursor: bool,
frame: &crate::state::FrameScene,
) -> Vec<AnimatedWindowRenderElement> {
scene_elements(state, renderer, output, include_cursor, frame, false)
}
pub(crate) fn capture_output_elements(
state: &mut Ferese,
renderer: &mut GlesRenderer,
output: &Output,
include_cursor: bool,
frame: &crate::state::FrameScene,
) -> Vec<AnimatedWindowRenderElement> {
// Blur textures and resize handoff snapshots must never be shared with
// display rendering. Only the capture scene can populate these caches.
std::mem::swap(&mut state.render, &mut state.capture_render);
let elements = scene_elements(state, renderer, output, include_cursor, frame, true);
std::mem::swap(&mut state.render, &mut state.capture_render);
elements
}
fn scene_elements(
state: &mut Ferese,
renderer: &mut GlesRenderer,
output: &Output,
include_cursor: bool,
frame: &crate::state::FrameScene,
capture: bool,
) -> Vec<AnimatedWindowRenderElement> {
if state.session_lock.active() {
let Some(geometry) = state.space.output_geometry(output) else {
@@ -89,7 +115,8 @@ pub(crate) fn sampled_output_elements(
};
let scale = output.current_scale().fractional_scale();
let mut elements = if include_cursor {
let protected_cursor = capture && state.capture_cursor_protected();
let mut elements = if include_cursor && !protected_cursor {
cursor_elements(state, renderer, output_geometry, scale)
} else {
Vec::new()
@@ -108,12 +135,13 @@ pub(crate) fn sampled_output_elements(
output_geometry,
scale,
frame,
capture,
));
}
let prepare_window = |window: &Window| {
// Configure immediately, but present the frame/shadow only after
// the first buffer commit has settled the actual client geometry.
if !state.window_content_ready(window) {
if (capture && state.capture_protected(window)) || !state.window_content_ready(window) {
return None;
}
let id = *state.windows.ids().get(window)?;