diff --git a/README.md b/README.md index c77c715..b0b57cc 100644 --- a/README.md +++ b/README.md @@ -71,9 +71,8 @@ FERESE_ENABLE_SCREENCOPY=1 cargo run -p ferese ``` The current M5 implementation supports one-shot output and region capture into -ARGB8888 shared-memory buffers. Captures currently include Ferese's software -cursor even when a client does not request a cursor overlay; cursor-free -capture remains part of the M5 interoperability acceptance work. +ARGB8888 shared-memory buffers. Cursor overlays are included only when the +capture client requests them. ### Direct DRM session diff --git a/compositor/src/backends/direct.rs b/compositor/src/backends/direct.rs index 81aa41a..55a5a25 100644 --- a/compositor/src/backends/direct.rs +++ b/compositor/src/backends/direct.rs @@ -38,7 +38,10 @@ use smithay::{ wayland::presentation::Refresh, }; -use crate::{Ferese, winit::animated_window_elements}; +use crate::{ + Ferese, + winit::{animated_window_elements, cursorless_window_elements, redraw_output}, +}; pub struct DirectBackendState { pub session: LibSeatSession, @@ -349,7 +352,27 @@ fn render_output(state: &mut Ferese, node: DrmNode, crtc: crtc::Handle) { &elements, [0.035, 0.04, 0.055, 1.0], )?; - if state.process_screencopies(&mut device.renderer, &framebuffer, &output.output) { + let cursorless_capture = state.has_pending_screencopy(&output.output, false); + let captured_with_cursor = + state.process_screencopies(&mut device.renderer, &framebuffer, &output.output, true); + + if cursorless_capture { + let cursorless_elements = + cursorless_window_elements(state, &mut device.renderer, &output.output); + redraw_output( + &mut device.renderer, + &mut framebuffer, + &output.output, + &cursorless_elements, + )?; + state.process_screencopies(&mut device.renderer, &framebuffer, &output.output, false); + redraw_output( + &mut device.renderer, + &mut framebuffer, + &output.output, + &elements, + )?; + } else if captured_with_cursor { let _ = device .renderer .render( diff --git a/compositor/src/handlers/screencopy.rs b/compositor/src/handlers/screencopy.rs index 53b3dec..c4c6d49 100644 --- a/compositor/src/handlers/screencopy.rs +++ b/compositor/src/handlers/screencopy.rs @@ -28,6 +28,7 @@ const BYTES_PER_PIXEL: usize = 4; pub(crate) struct FrameData { output: Option, region: Rectangle, + overlay_cursor: bool, used: Mutex, } @@ -37,6 +38,7 @@ pub(crate) struct PendingScreencopy { buffer: WlBuffer, pub(crate) output: Output, region: Rectangle, + overlay_cursor: bool, with_damage: bool, } @@ -75,14 +77,14 @@ impl Dispatch for Ferese { match request { zwlr_screencopy_manager_v1::Request::CaptureOutput { frame, - overlay_cursor: _, + overlay_cursor, output, } => { - create_frame(data_init, frame, output, None); + create_frame(data_init, frame, output, None, overlay_cursor != 0); } zwlr_screencopy_manager_v1::Request::CaptureOutputRegion { frame, - overlay_cursor: _, + overlay_cursor, output, x, y, @@ -94,6 +96,7 @@ impl Dispatch for Ferese { frame, output, Some(Rectangle::new((x, y).into(), (width, height).into())), + overlay_cursor != 0, ); } zwlr_screencopy_manager_v1::Request::Destroy => {} @@ -161,6 +164,7 @@ impl Ferese { buffer, output, region: data.region, + overlay_cursor: data.overlay_cursor, with_damage, }); crate::backends::direct::render_all(self); @@ -171,6 +175,7 @@ impl Ferese { renderer: &mut R, framebuffer: &R::Framebuffer<'_>, output: &Output, + overlay_cursor: bool, ) -> bool where R: ExportMem, @@ -180,7 +185,9 @@ impl Ferese { let captures = std::mem::take(&mut self.pending_screencopies); for capture in captures { - if &capture.output != output { + if &capture.output != output + || !cursor_overlay_matches(capture.overlay_cursor, overlay_cursor) + { remaining.push(capture); continue; } @@ -198,6 +205,17 @@ impl Ferese { self.pending_screencopies = remaining; framebuffer_binding_changed } + + pub(crate) fn has_pending_screencopy(&self, output: &Output, overlay_cursor: bool) -> bool { + self.pending_screencopies.iter().any(|capture| { + capture.output == *output + && cursor_overlay_matches(capture.overlay_cursor, overlay_cursor) + }) + } +} + +fn cursor_overlay_matches(requested: bool, rendered: bool) -> bool { + requested == rendered } fn create_frame( @@ -205,6 +223,7 @@ fn create_frame( frame: New, wl_output: smithay::reexports::wayland_server::protocol::wl_output::WlOutput, requested: Option>, + overlay_cursor: bool, ) { let Some(output) = Output::from_resource(&wl_output) else { let frame = data_init.init( @@ -212,6 +231,7 @@ fn create_frame( Arc::new(FrameData { output: None, region: Rectangle::from_size((1, 1).into()), + overlay_cursor, used: Mutex::new(true), }), ); @@ -224,6 +244,7 @@ fn create_frame( Arc::new(FrameData { output: Some(output), region: Rectangle::from_size((1, 1).into()), + overlay_cursor, used: Mutex::new(true), }), ); @@ -235,6 +256,7 @@ fn create_frame( let data = Arc::new(FrameData { output: Some(output), region, + overlay_cursor, used: Mutex::new(false), }); let frame = data_init.init(frame, data); @@ -392,4 +414,12 @@ mod tests { capture_region_for_geometry(mode, 2.0, Transform::Normal, Some(requested),).is_none() ); } + + #[test] + fn cursor_overlay_requests_use_only_the_matching_render_pass() { + assert!(cursor_overlay_matches(false, false)); + assert!(cursor_overlay_matches(true, true)); + assert!(!cursor_overlay_matches(false, true)); + assert!(!cursor_overlay_matches(true, false)); + } } diff --git a/compositor/src/winit.rs b/compositor/src/winit.rs index 5930952..8d1ad9b 100644 --- a/compositor/src/winit.rs +++ b/compositor/src/winit.rs @@ -6,10 +6,10 @@ use std::{ use smithay::{ backend::{ renderer::{ - Frame, ImportDma, Renderer, + Color32F, Frame, ImportDma, Renderer, damage::OutputDamageTracker, element::{ - AsRenderElements, Kind as RenderElementKind, + AsRenderElements, Kind as RenderElementKind, RenderElement, memory::MemoryRenderBufferRenderElement, render_elements, surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree}, @@ -19,6 +19,7 @@ use smithay::{ }, }, gles::GlesRenderer, + utils::draw_render_elements, }, winit::{self, WinitEvent}, }, @@ -143,7 +144,22 @@ pub fn init(event_loop: &mut EventLoop, state: &mut Ferese) -> Result<() &elements, [0.035, 0.04, 0.055, 1.0], )?; - if state.process_screencopies(renderer, &framebuffer, &output) { + 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 { let _ = renderer .render( &mut framebuffer, @@ -203,13 +219,34 @@ pub(crate) fn animated_window_elements( state: &Ferese, renderer: &mut GlesRenderer, output: &Output, +) -> Vec { + output_elements(state, renderer, output, true) +} + +pub(crate) fn cursorless_window_elements( + state: &Ferese, + renderer: &mut GlesRenderer, + output: &Output, +) -> Vec { + output_elements(state, renderer, output, false) +} + +fn output_elements( + state: &Ferese, + renderer: &mut GlesRenderer, + output: &Output, + include_cursor: bool, ) -> Vec { let Some(output_geometry) = state.space.output_geometry(output) else { return Vec::new(); }; let scale = output.current_scale().fractional_scale(); - let mut elements = cursor_elements(state, renderer, output_geometry, scale); + let mut elements = if include_cursor { + cursor_elements(state, renderer, output_geometry, scale) + } else { + Vec::new() + }; elements.extend(layer_elements( renderer, output, @@ -264,6 +301,34 @@ pub(crate) fn animated_window_elements( elements } +pub(crate) fn redraw_output( + renderer: &mut R, + framebuffer: &mut R::Framebuffer<'_>, + output: &Output, + elements: &[E], +) -> Result<(), R::Error> +where + R: Renderer, + R::TextureId: 'static, + E: RenderElement, +{ + let mode = output.current_mode().expect("output has a mode"); + let transform = output.current_transform().invert(); + let damage = Rectangle::from_size(transform.transform_size(mode.size)); + let mut frame = renderer.render(framebuffer, mode.size, transform)?; + + frame.clear(Color32F::new(0.035, 0.04, 0.055, 1.0), &[damage])?; + draw_render_elements( + &mut frame, + output.current_scale().fractional_scale(), + elements, + &[damage], + )?; + let _ = frame.finish()?; + + Ok(()) +} + fn layer_surfaces(output: &Output) -> Vec { layer_map_for_output(output).layers().cloned().collect() }