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:
1 parent
bffd538dd4
commit
453ec16583
24 files changed
+1132
-41
No files matched your search
@@ -23,7 +23,7 @@ use overview::*;
|
||||
pub(crate) use resources::RenderResources;
|
||||
use scene::cursor_elements;
|
||||
pub(crate) use scene::{
|
||||
animated_window_elements, frame_effect_metrics, layer_surfaces, output_elements, sampled_output_elements,
|
||||
animated_window_elements, capture_output_elements, frame_effect_metrics, layer_surfaces, sampled_output_elements,
|
||||
};
|
||||
pub(crate) use shader::SharedPixelShaderElement;
|
||||
use smithay::backend::allocator::Fourcc;
|
||||
|
||||
@@ -91,6 +91,7 @@ pub(super) fn overview_strip_elements(
|
||||
output_geometry: Rectangle<i32, Logical>,
|
||||
scale: f64,
|
||||
frame: &crate::state::FrameScene,
|
||||
capture: bool,
|
||||
) -> Vec<AnimatedWindowRenderElement> {
|
||||
let alpha = frame.overview.opacity();
|
||||
if alpha <= 0.001 {
|
||||
@@ -139,7 +140,13 @@ pub(super) fn overview_strip_elements(
|
||||
// has a fresh seed; iterating it changes z indices and damages still captions.
|
||||
let caption_ids = state.windows.ordered_ids().rev().collect::<Vec<_>>();
|
||||
for id in caption_ids {
|
||||
if !state.window_belongs_to_output(id, output) {
|
||||
if (capture
|
||||
&& state
|
||||
.windows
|
||||
.window(id)
|
||||
.is_none_or(|window| state.capture_protected(window)))
|
||||
|| !state.window_belongs_to_output(id, output)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let Some(rect) = frame.windows.get(&id).map(|sample| sample.presentation.bounds.current) else {
|
||||
@@ -222,6 +229,9 @@ pub(super) fn overview_strip_elements(
|
||||
}
|
||||
for (id, rect) in &card.windows {
|
||||
if let Some(window) = state.windows.window(*id) {
|
||||
if capture && state.capture_protected(window) {
|
||||
continue;
|
||||
}
|
||||
let shape = window_corner_shape(window);
|
||||
|
||||
if let Some(programs) = corner_program(&mut state.render, renderer, shape) {
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
Reference in new issue
Block a user