diff options
| author | srdusr <[email protected]> | 2024-05-30 16:10:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2024-05-30 16:10:00 +0200 |
| commit | 1c175642d073689ca11b9252411ea5f8446007d0 (patch) | |
| tree | 00b8f9062cf1261d2d2642c059a14ff5790522e5 /crates/wayland/src/screencopy.rs | |
| parent | 3d3057ae384ef7389284af8988410889e99c6bb9 (diff) | |
| download | srdwm-1c175642d073689ca11b9252411ea5f8446007d0.tar.gz srdwm-1c175642d073689ca11b9252411ea5f8446007d0.zip | |
Fix decoration drift during animated transitions; checkpoint IPC/global-menu/output-management work
Border/titlebar decoration was built from Window.geometry (the animation's
final target) in both wayland backends' render loops, while sync_geometry
already draws a window's actual content at window_anims' interpolated rect
during any maximize/fullscreen/open-slide tween. Border and content read two
different rectangles for the whole transition, so the border visibly
detached from the window it was outlining - reported as "borders aren't
flush." Both udev.rs and winit.rs now read the same animated rect for
titlebar placement, border-strip placement, and the occlusion test against
later windows in stacking order. Verified: cargo build --workspace, cargo
clippy (0 new warnings), cargo test -p srdwm-core (111/111).
Also checkpoints substantial protocol/IPC work from prior sessions that had
accumulated uncommitted: gtk-shell1 support (gtk_shell.rs, the vendored
gtk-shell.xml, xwayland.rs's X11-side mirror) backing the global app menu;
zwlr_foreign_toplevel_manager_v1 (foreign_toplevel.rs) broadcasting distinct
maximized/minimized/fullscreen/activated state per window; output_management
(ext-output-management + layer-shell exclusive-zone reservation tracking);
workspace.rs and context_menu.rs; a Unix-socket IPC crate (platform/src/
ipc.rs) and a `srd` control-CLI crate (crates/ctl); xkb_config.rs; and a
theme module (core/src/theme.rs). A peer session working the AGS shell
concurrently verified several of these live against a running srdwm: the
global menu rendering a real app's File/Edit menu over gtk-shell1, and
foreign-toplevel correctly reporting maximized and fullscreen as independent,
non-simultaneous states with the geometry each implies (maximize stops at a
reserved top bar and past a dock; fullscreen reaches the true monitor edge).
Diffstat (limited to 'crates/wayland/src/screencopy.rs')
| -rw-r--r-- | crates/wayland/src/screencopy.rs | 35 |
1 files changed, 29 insertions, 6 deletions
diff --git a/crates/wayland/src/screencopy.rs b/crates/wayland/src/screencopy.rs index 324669c..cb632aa 100644 --- a/crates/wayland/src/screencopy.rs +++ b/crates/wayland/src/screencopy.rs @@ -21,6 +21,7 @@ use std::time::UNIX_EPOCH; use smithay::backend::allocator::Fourcc; use smithay::backend::renderer::{ExportMem, Renderer}; +use smithay::output::Output; use smithay::reexports::wayland_server::protocol::wl_buffer::WlBuffer; use smithay::reexports::wayland_server::protocol::wl_output::WlOutput; use smithay::reexports::wayland_server::protocol::wl_shm; @@ -66,6 +67,11 @@ impl ScreencopyState { pub struct FrameData { /// Region of the output to capture, in physical pixels. pub region: Rectangle<i32, Physical>, + /// The output this frame captures. `None` if the `wl_output` the client + /// named at request time doesn't resolve to a live output (e.g. + /// unplugged between bind and capture) - such a frame is failed + /// immediately and never queued, so this is only read on that path. + pub output: Option<Output>, /// Set once `copy`/`copy_with_damage` has been handled, so a second one /// can be rejected with the protocol's `already_used` error. pub used: bool, @@ -78,6 +84,11 @@ pub struct PendingCapture { pub frame: ZwlrScreencopyFrameV1, pub buffer: WlBuffer, pub region: Rectangle<i32, Physical>, + /// Which head this capture is bound to - the udev backend renders each + /// head into its own framebuffer, so a capture must be serviced against + /// the framebuffer for *this* output, not whichever head happens to + /// render first (see `render_udev_frame`'s per-output split). + pub output: Output, /// `copy_with_damage` clients expect a `damage` event before `ready`. pub with_damage: bool, } @@ -106,9 +117,9 @@ impl Dispatch<ZwlrScreencopyManagerV1, ()> for CompState { data_init: &mut DataInit<'_, Self>, ) { use zwlr_screencopy_manager_v1::Request; - let (frame, region) = match request { + let (frame, region, output) = match request { Request::CaptureOutput { frame, overlay_cursor: _, output } => { - (frame, state.output_capture_region(&output)) + (frame, state.output_capture_region(&output), state.output_for_wl(&output).map(|e| e.output.clone())) } Request::CaptureOutputRegion { frame, overlay_cursor: _, output, x, y, width, height } => { // Clamp to the output: a client is free to ask for a region @@ -116,15 +127,17 @@ impl Dispatch<ZwlrScreencopyManagerV1, ()> for CompState { // `copy_framebuffer` errors out on out-of-bounds reads. let full = state.output_capture_region(&output); let requested = Rectangle::new((x, y).into(), (width.max(0), height.max(0)).into()); - (frame, full.intersection(requested).unwrap_or_default()) + let region = full.intersection(requested).unwrap_or_default(); + (frame, region, state.output_for_wl(&output).map(|e| e.output.clone())) } Request::Destroy => return, _ => return, }; - let frame = data_init.init(frame, FrameData { region, used: false }); - if region.size.w <= 0 || region.size.h <= 0 { - // Nothing to capture (empty or fully off-screen region). + let frame = data_init.init(frame, FrameData { region, output: output.clone(), used: false }); + if region.size.w <= 0 || region.size.h <= 0 || output.is_none() { + // Nothing to capture: an empty/off-screen region, or a + // `wl_output` that no longer resolves to a live head. frame.failed(); return; } @@ -182,10 +195,20 @@ impl Dispatch<ZwlrScreencopyFrameV1, FrameData> for CompState { return; } + // A frame with no resolved output was already failed at request + // time (see the manager's `request` handler) and should never reach + // `Copy`/`CopyWithDamage` from a well-behaved client; guarded rather + // than unwrapped so a misbehaving one can't panic the compositor. + let Some(output) = data.output.clone() else { + frame.post_error(zwlr_screencopy_frame_v1::Error::InvalidBuffer, "frame has no output to capture"); + return; + }; + state.screencopy_pending.push(PendingCapture { frame: frame.clone(), buffer, region: data.region, + output, with_damage, }); } |