fix: honor screencopy cursor overlay requests
This commit is contained in:
1 parent
4a1eab5b4a
commit
f0862348c9
4 files changed
+130
-13
No files matched your search
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user