From 4c107130734bde3d33b75c9270a153a81df4767e Mon Sep 17 00:00:00 2001 From: atagen Date: Sun, 31 May 2026 17:09:23 +1000 Subject: [PATCH] Fix screencopy and portal capture state handling --- .../ext_image_copy_capture_frame_v1.rs | 23 +++++++-- .../ext_image_copy_capture_session_v1.rs | 5 ++ src/ifs/zwlr_screencopy_frame_v1.rs | 49 +++++++++++++------ src/ifs/zwlr_screencopy_manager_v1.rs | 4 +- src/portal/ptl_screencast.rs | 19 +++++-- src/tree/output.rs | 12 +++-- 6 files changed, 85 insertions(+), 27 deletions(-) diff --git a/src/ifs/ext_image_copy/ext_image_copy_capture_frame_v1.rs b/src/ifs/ext_image_copy/ext_image_copy_capture_frame_v1.rs index 48fa9038..786e5d47 100644 --- a/src/ifs/ext_image_copy/ext_image_copy_capture_frame_v1.rs +++ b/src/ifs/ext_image_copy/ext_image_copy_capture_frame_v1.rs @@ -86,9 +86,7 @@ impl ExtImageCopyCaptureFrameV1 { let buffer = self.session.buffer.get().unwrap(); if size != buffer.rect.size() { self.session.buffer_size_changed(); - // https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/222 - // self.fail(FrameFailureReason::BufferConstraints); - // return; + return Err(FrameFailureReason::BufferConstraints); } if let Err(e) = buffer.update_framebuffer() { log::error!("Could not import buffer: {}", ErrorFmt(e)); @@ -102,6 +100,13 @@ impl ExtImageCopyCaptureFrameV1 { let mut shm_staging = self.session.shm_staging.take(); match storage { WlBufferStorage::Shm { mem, stride, .. } => { + log::debug!( + "ext-image-copy frame {:?} using wl_shm readback path: {}x{}, stride {}", + self.id, + buffer.rect.width(), + buffer.rect.height(), + *stride, + ); if let Some(b) = &shm_bridge && (b.physical_size() != buffer.rect.size() || b.format() != buffer.format @@ -159,6 +164,12 @@ impl ExtImageCopyCaptureFrameV1 { self.session.shm_staging.set(Some(staging)); } WlBufferStorage::Dmabuf { fb, .. } => { + log::debug!( + "ext-image-copy frame {:?} using dmabuf GPU copy path: {}x{}", + self.id, + buffer.rect.width(), + buffer.rect.height(), + ); let Some(fb) = fb else { return Err(FrameFailureReason::BufferConstraints); }; @@ -187,7 +198,11 @@ impl ExtImageCopyCaptureFrameV1 { ) { match self.try_copy(on, size, f) { Ok(()) => self.session.status.set(FrameStatus::Captured), - Err(e) => self.fail(e), + Err(e) => { + if self.session.status.get() != FrameStatus::Failed { + self.fail(e); + } + } } } diff --git a/src/ifs/ext_image_copy/ext_image_copy_capture_session_v1.rs b/src/ifs/ext_image_copy/ext_image_copy_capture_session_v1.rs index 286e9850..eb7154e7 100644 --- a/src/ifs/ext_image_copy/ext_image_copy_capture_session_v1.rs +++ b/src/ifs/ext_image_copy/ext_image_copy_capture_session_v1.rs @@ -83,6 +83,11 @@ impl ExtImageCopyCaptureSessionV1 { if self.size_debounce.replace(true) { return; } + if let Some(frame) = self.frame.get() + && let FrameStatus::Capturing | FrameStatus::Captured = self.status.get() + { + frame.fail(FrameFailureReason::BufferConstraints); + } self.force_capture.set(true); self.send_current_buffer_size(); self.send_done(); diff --git a/src/ifs/zwlr_screencopy_frame_v1.rs b/src/ifs/zwlr_screencopy_frame_v1.rs index d66035e9..d27b9c29 100644 --- a/src/ifs/zwlr_screencopy_frame_v1.rs +++ b/src/ifs/zwlr_screencopy_frame_v1.rs @@ -48,16 +48,13 @@ impl ZwlrScreencopyFrameV1 { } pub fn send_damage(&self) { - if let Some(output) = self.output.get() { - let pos = output.pos.get(); - self.client.event(Damage { - self_id: self.id, - x: 0, - y: 0, - width: pos.width() as _, - height: pos.height() as _, - }); - } + self.client.event(Damage { + self_id: self.id, + x: 0, + y: 0, + width: self.rect.width() as _, + height: self.rect.height() as _, + }); } pub fn send_buffer(&self) { @@ -111,10 +108,28 @@ impl ZwlrScreencopyFrameV1 { return Err(ZwlrScreencopyFrameV1Error::InvalidBufferFormat); } buffer.update_framebuffer()?; - if let Some(WlBufferStorage::Shm { stride, .. }) = buffer.storage.borrow_mut().deref() - && *stride != self.rect.width() * 4 - { - return Err(ZwlrScreencopyFrameV1Error::InvalidBufferStride); + match buffer.storage.borrow_mut().deref() { + Some(WlBufferStorage::Shm { stride, .. }) => { + if *stride != self.rect.width() * 4 { + return Err(ZwlrScreencopyFrameV1Error::InvalidBufferStride); + } + log::debug!( + "zwlr_screencopy frame {:?} using wl_shm readback path: {}x{}, stride {}", + self.id, + self.rect.width(), + self.rect.height(), + *stride, + ); + } + Some(WlBufferStorage::Dmabuf { .. }) => { + log::debug!( + "zwlr_screencopy frame {:?} using dmabuf GPU copy path: {}x{}", + self.id, + self.rect.width(), + self.rect.height(), + ); + } + _ => {} } self.buffer.set(Some(buffer)); if !with_damage && let Some(global) = self.output.get() { @@ -134,6 +149,12 @@ impl ZwlrScreencopyFrameV1 { } self.pending.take(); } + + pub fn cancel(&self) { + self.buffer.take(); + self.pending.take(); + self.send_failed(); + } } impl ZwlrScreencopyFrameV1RequestHandler for ZwlrScreencopyFrameV1 { diff --git a/src/ifs/zwlr_screencopy_manager_v1.rs b/src/ifs/zwlr_screencopy_manager_v1.rs index 2ff6c8a4..5f70072f 100644 --- a/src/ifs/zwlr_screencopy_manager_v1.rs +++ b/src/ifs/zwlr_screencopy_manager_v1.rs @@ -104,8 +104,8 @@ impl ZwlrScreencopyManagerV1 { let Some(global) = output.global.get() else { return Ok(()); }; - let mode = global.mode.get(); - let mut rect = Rect::new_sized_saturating(0, 0, mode.width, mode.height); + let (width, height) = global.pixel_size(); + let mut rect = Rect::new_sized_saturating(0, 0, width, height); if let Some(region) = region { let scale = global.persistent.scale.get().to_f64(); let x1 = (region.x1() as f64 * scale).round() as i32; diff --git a/src/portal/ptl_screencast.rs b/src/portal/ptl_screencast.rs index d3563f0f..20d21f4a 100644 --- a/src/portal/ptl_screencast.rs +++ b/src/portal/ptl_screencast.rs @@ -315,6 +315,14 @@ impl PwClientNodeOwner for StartedScreencast { } } } + log::debug!( + "Portal screencast using PipeWire dmabuf GPU copy path: {} buffers, format {}, modifier 0x{:08x}, size {}x{}", + self.buffers.borrow().len(), + self.format.get().name, + self.modifier.get(), + self.width.get(), + self.height.get(), + ); self.node .send_port_output_buffers(&self.port, &self.buffers.borrow()); } @@ -633,15 +641,18 @@ impl UsrJayScreencastOwner for StartedScreencast { fn ready(&self, ev: &Ready) { let idx = ev.idx as usize; - let buffers = &*self.buffers.borrow(); - let pbuffers = self.port.buffers.borrow(); - let buffer = &buffers[idx]; let discard_buffer = || { self.jay_screencast.release_buffer(idx); }; if !self.buffers_valid.get() { return; } + let buffers = self.buffers.borrow(); + let Some(buffer) = buffers.get(idx) else { + log::warn!("Ignoring ready event for unknown screencast buffer {idx}"); + return; + }; + let pbuffers = self.port.buffers.borrow(); let Some(io) = self.port.io_buffers.get() else { discard_buffer(); return; @@ -767,7 +778,7 @@ pub(super) fn add_screencast_dbus_members( object.add_method::(move |req, pr| { dbus_start(&state, req, pr); }); - object.set_property::(Variant::U32(MONITOR.0)); + object.set_property::(Variant::U32((MONITOR | WINDOW).0)); object.set_property::(Variant::U32(EMBEDDED.0)); object.set_property::(Variant::U32(5)); } diff --git a/src/tree/output.rs b/src/tree/output.rs index d21b3e5e..456e7a5d 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -460,9 +460,15 @@ impl OutputNode { } self.lock_surface.take(); self.jay_outputs.clear(); - self.screencasts.clear(); - self.screencopies.clear(); - self.ext_copy_sessions.clear(); + for screencast in self.screencasts.lock().drain_values() { + screencast.do_destroy(); + } + for screencopy in self.screencopies.lock().drain_values() { + screencopy.cancel(); + } + for session in self.ext_copy_sessions.lock().drain_values() { + session.stop(); + } self.ext_workspace_groups.clear(); self.latch_event.clear(); self.vblank_event.clear();