fix: honor screencopy cursor overlay requests

This commit is contained in:
Samuel Onoja committed 2026-09-25 02:31:18 +01:00
1 parent 4a1eab5b4a
commit f0862348c9
4 files changed
+130 -13

No files matched your search

+2 -3
View File
@@ -71,9 +71,8 @@ FERESE_ENABLE_SCREENCOPY=1 cargo run -p ferese
``` ```
The current M5 implementation supports one-shot output and region capture into The current M5 implementation supports one-shot output and region capture into
ARGB8888 shared-memory buffers. Captures currently include Ferese's software ARGB8888 shared-memory buffers. Cursor overlays are included only when the
cursor even when a client does not request a cursor overlay; cursor-free capture client requests them.
capture remains part of the M5 interoperability acceptance work.
### Direct DRM session ### Direct DRM session
+25 -2
View File
@@ -38,7 +38,10 @@ use smithay::{
wayland::presentation::Refresh, 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 struct DirectBackendState {
pub session: LibSeatSession, pub session: LibSeatSession,
@@ -349,7 +352,27 @@ fn render_output(state: &mut Ferese, node: DrmNode, crtc: crtc::Handle) {
&elements, &elements,
[0.035, 0.04, 0.055, 1.0], [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 let _ = device
.renderer .renderer
.render( .render(
+34 -4
View File
@@ -28,6 +28,7 @@ const BYTES_PER_PIXEL: usize = 4;
pub(crate) struct FrameData { pub(crate) struct FrameData {
output: Option<Output>, output: Option<Output>,
region: Rectangle<i32, Buffer>, region: Rectangle<i32, Buffer>,
overlay_cursor: bool,
used: Mutex<bool>, used: Mutex<bool>,
} }
@@ -37,6 +38,7 @@ pub(crate) struct PendingScreencopy {
buffer: WlBuffer, buffer: WlBuffer,
pub(crate) output: Output, pub(crate) output: Output,
region: Rectangle<i32, Buffer>, region: Rectangle<i32, Buffer>,
overlay_cursor: bool,
with_damage: bool, with_damage: bool,
} }
@@ -75,14 +77,14 @@ impl Dispatch<ZwlrScreencopyManagerV1, ()> for Ferese {
match request { match request {
zwlr_screencopy_manager_v1::Request::CaptureOutput { zwlr_screencopy_manager_v1::Request::CaptureOutput {
frame, frame,
overlay_cursor: _, overlay_cursor,
output, output,
} => { } => {
create_frame(data_init, frame, output, None); create_frame(data_init, frame, output, None, overlay_cursor != 0);
} }
zwlr_screencopy_manager_v1::Request::CaptureOutputRegion { zwlr_screencopy_manager_v1::Request::CaptureOutputRegion {
frame, frame,
overlay_cursor: _, overlay_cursor,
output, output,
x, x,
y, y,
@@ -94,6 +96,7 @@ impl Dispatch<ZwlrScreencopyManagerV1, ()> for Ferese {
frame, frame,
output, output,
Some(Rectangle::new((x, y).into(), (width, height).into())), Some(Rectangle::new((x, y).into(), (width, height).into())),
overlay_cursor != 0,
); );
} }
zwlr_screencopy_manager_v1::Request::Destroy => {} zwlr_screencopy_manager_v1::Request::Destroy => {}
@@ -161,6 +164,7 @@ impl Ferese {
buffer, buffer,
output, output,
region: data.region, region: data.region,
overlay_cursor: data.overlay_cursor,
with_damage, with_damage,
}); });
crate::backends::direct::render_all(self); crate::backends::direct::render_all(self);
@@ -171,6 +175,7 @@ impl Ferese {
renderer: &mut R, renderer: &mut R,
framebuffer: &R::Framebuffer<'_>, framebuffer: &R::Framebuffer<'_>,
output: &Output, output: &Output,
overlay_cursor: bool,
) -> bool ) -> bool
where where
R: ExportMem, R: ExportMem,
@@ -180,7 +185,9 @@ impl Ferese {
let captures = std::mem::take(&mut self.pending_screencopies); let captures = std::mem::take(&mut self.pending_screencopies);
for capture in captures { for capture in captures {
if &capture.output != output { if &capture.output != output
|| !cursor_overlay_matches(capture.overlay_cursor, overlay_cursor)
{
remaining.push(capture); remaining.push(capture);
continue; continue;
} }
@@ -198,6 +205,17 @@ impl Ferese {
self.pending_screencopies = remaining; self.pending_screencopies = remaining;
framebuffer_binding_changed 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( fn create_frame(
@@ -205,6 +223,7 @@ fn create_frame(
frame: New<ZwlrScreencopyFrameV1>, frame: New<ZwlrScreencopyFrameV1>,
wl_output: smithay::reexports::wayland_server::protocol::wl_output::WlOutput, wl_output: smithay::reexports::wayland_server::protocol::wl_output::WlOutput,
requested: Option<Rectangle<i32, Logical>>, requested: Option<Rectangle<i32, Logical>>,
overlay_cursor: bool,
) { ) {
let Some(output) = Output::from_resource(&wl_output) else { let Some(output) = Output::from_resource(&wl_output) else {
let frame = data_init.init( let frame = data_init.init(
@@ -212,6 +231,7 @@ fn create_frame(
Arc::new(FrameData { Arc::new(FrameData {
output: None, output: None,
region: Rectangle::from_size((1, 1).into()), region: Rectangle::from_size((1, 1).into()),
overlay_cursor,
used: Mutex::new(true), used: Mutex::new(true),
}), }),
); );
@@ -224,6 +244,7 @@ fn create_frame(
Arc::new(FrameData { Arc::new(FrameData {
output: Some(output), output: Some(output),
region: Rectangle::from_size((1, 1).into()), region: Rectangle::from_size((1, 1).into()),
overlay_cursor,
used: Mutex::new(true), used: Mutex::new(true),
}), }),
); );
@@ -235,6 +256,7 @@ fn create_frame(
let data = Arc::new(FrameData { let data = Arc::new(FrameData {
output: Some(output), output: Some(output),
region, region,
overlay_cursor,
used: Mutex::new(false), used: Mutex::new(false),
}); });
let frame = data_init.init(frame, data); 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() 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));
}
} }
+69 -4
View File
@@ -6,10 +6,10 @@ use std::{
use smithay::{ use smithay::{
backend::{ backend::{
renderer::{ renderer::{
Frame, ImportDma, Renderer, Color32F, Frame, ImportDma, Renderer,
damage::OutputDamageTracker, damage::OutputDamageTracker,
element::{ element::{
AsRenderElements, Kind as RenderElementKind, AsRenderElements, Kind as RenderElementKind, RenderElement,
memory::MemoryRenderBufferRenderElement, memory::MemoryRenderBufferRenderElement,
render_elements, render_elements,
surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree}, surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree},
@@ -19,6 +19,7 @@ use smithay::{
}, },
}, },
gles::GlesRenderer, gles::GlesRenderer,
utils::draw_render_elements,
}, },
winit::{self, WinitEvent}, winit::{self, WinitEvent},
}, },
@@ -143,7 +144,22 @@ pub fn init(event_loop: &mut EventLoop<Ferese>, state: &mut Ferese) -> Result<()
&elements, &elements,
[0.035, 0.04, 0.055, 1.0], [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 let _ = renderer
.render( .render(
&mut framebuffer, &mut framebuffer,
@@ -203,13 +219,34 @@ pub(crate) fn animated_window_elements(
state: &Ferese, state: &Ferese,
renderer: &mut GlesRenderer, renderer: &mut GlesRenderer,
output: &Output, output: &Output,
) -> Vec<AnimatedWindowRenderElement> {
output_elements(state, renderer, output, true)
}
pub(crate) fn cursorless_window_elements(
state: &Ferese,
renderer: &mut GlesRenderer,
output: &Output,
) -> Vec<AnimatedWindowRenderElement> {
output_elements(state, renderer, output, false)
}
fn output_elements(
state: &Ferese,
renderer: &mut GlesRenderer,
output: &Output,
include_cursor: bool,
) -> Vec<AnimatedWindowRenderElement> { ) -> Vec<AnimatedWindowRenderElement> {
let Some(output_geometry) = state.space.output_geometry(output) else { let Some(output_geometry) = state.space.output_geometry(output) else {
return Vec::new(); return Vec::new();
}; };
let scale = output.current_scale().fractional_scale(); 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( elements.extend(layer_elements(
renderer, renderer,
output, output,
@@ -264,6 +301,34 @@ pub(crate) fn animated_window_elements(
elements elements
} }
pub(crate) fn redraw_output<R, E>(
renderer: &mut R,
framebuffer: &mut R::Framebuffer<'_>,
output: &Output,
elements: &[E],
) -> Result<(), R::Error>
where
R: Renderer,
R::TextureId: 'static,
E: RenderElement<R>,
{
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<LayerSurface> { fn layer_surfaces(output: &Output) -> Vec<LayerSurface> {
layer_map_for_output(output).layers().cloned().collect() layer_map_for_output(output).layers().cloned().collect()
} }