aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichael Yang <admin@my4ng.dev>2024-07-30 13:38:25 +1000
committerIvan Molodetskikh <yalterz@gmail.com>2024-08-08 13:32:37 +0300
commitb6a7b3e9e4715c099b1d0e67252c0a149238d683 (patch)
treee0f93b6c04c1f1e136c946b64dcd4b5647579f79 /src
parent1cf5cfce064ba9926b9777df34cb5a4add0e1f64 (diff)
downloadniri-b6a7b3e9e4715c099b1d0e67252c0a149238d683.tar.gz
niri-b6a7b3e9e4715c099b1d0e67252c0a149238d683.tar.bz2
niri-b6a7b3e9e4715c099b1d0e67252c0a149238d683.zip
feat: update screencopy to version 3
Diffstat (limited to 'src')
-rw-r--r--src/handlers/mod.rs31
-rw-r--r--src/niri.rs223
-rw-r--r--src/protocols/screencopy.rs228
-rw-r--r--src/render_helpers/mod.rs36
4 files changed, 375 insertions, 143 deletions
diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs
index ce2896b2..b5c4392d 100644
--- a/src/handlers/mod.rs
+++ b/src/handlers/mod.rs
@@ -17,6 +17,7 @@ use smithay::input::{keyboard, Seat, SeatHandler, SeatState};
use smithay::output::Output;
use smithay::reexports::rustix::fs::{fcntl_setfl, OFlags};
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
+use smithay::reexports::wayland_protocols_wlr::screencopy::v1::server::zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1;
use smithay::reexports::wayland_server::protocol::wl_data_source::WlDataSource;
use smithay::reexports::wayland_server::protocol::wl_output::WlOutput;
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
@@ -69,7 +70,7 @@ use crate::protocols::foreign_toplevel::{
};
use crate::protocols::gamma_control::{GammaControlHandler, GammaControlManagerState};
use crate::protocols::output_management::{OutputManagementHandler, OutputManagementManagerState};
-use crate::protocols::screencopy::{Screencopy, ScreencopyHandler};
+use crate::protocols::screencopy::{Screencopy, ScreencopyHandler, ScreencopyManagerState};
use crate::utils::{output_size, send_scale_transform};
use crate::{
delegate_foreign_toplevel, delegate_gamma_control, delegate_output_management,
@@ -419,14 +420,30 @@ impl ForeignToplevelHandler for State {
delegate_foreign_toplevel!(State);
impl ScreencopyHandler for State {
- fn frame(&mut self, screencopy: Screencopy) {
- if let Err(err) = self
- .niri
- .render_for_screencopy(&mut self.backend, screencopy)
- {
- warn!("error rendering for screencopy: {err:?}");
+ fn frame(&mut self, manager: &ZwlrScreencopyManagerV1, screencopy: Screencopy) {
+ // If with_damage then push it onto the queue for redraw of the output,
+ // otherwise render it immediately.
+ if screencopy.with_damage() {
+ let Some(queue) = self.niri.screencopy_state.get_queue_mut(manager) else {
+ trace!("screencopy manager destroyed already");
+ return;
+ };
+ queue.push(screencopy);
+ } else {
+ self.backend.with_primary_renderer(|renderer| {
+ if let Err(err) = self
+ .niri
+ .render_for_screencopy_without_damage(renderer, manager, screencopy)
+ {
+ warn!("error rendering for screencopy: {err:?}");
+ }
+ });
}
}
+
+ fn screencopy_state(&mut self) -> &mut ScreencopyManagerState {
+ &mut self.niri.screencopy_state
+ }
}
delegate_screencopy!(State);
diff --git a/src/niri.rs b/src/niri.rs
index 7ee4d2f7..d5103eed 100644
--- a/src/niri.rs
+++ b/src/niri.rs
@@ -1,4 +1,4 @@
-use std::cell::{Cell, RefCell};
+use std::cell::{Cell, OnceCell, RefCell};
use std::collections::{HashMap, HashSet};
use std::ffi::OsString;
use std::path::PathBuf;
@@ -9,7 +9,7 @@ use std::time::{Duration, Instant};
use std::{env, mem, thread};
use _server_decoration::server::org_kde_kwin_server_decoration_manager::Mode as KdeDecorationsMode;
-use anyhow::{ensure, Context};
+use anyhow::{bail, ensure, Context};
use calloop::futures::Scheduler;
use niri_config::{
Config, FloatOrInt, Key, Modifiers, PreviewRender, TrackLayout, WorkspaceReference,
@@ -31,6 +31,7 @@ use smithay::backend::renderer::element::{
PrimaryScanoutOutput, RenderElementStates,
};
use smithay::backend::renderer::gles::GlesRenderer;
+use smithay::backend::renderer::Unbind;
use smithay::desktop::utils::{
bbox_from_surface_tree, output_update, send_dmabuf_feedback_surface_tree,
send_frames_surface_tree, surface_presentation_feedback_flags_from_states,
@@ -44,7 +45,7 @@ use smithay::desktop::{
use smithay::input::keyboard::{Layout as KeyboardLayout, XkbContextHandler};
use smithay::input::pointer::{CursorIcon, CursorImageAttributes, CursorImageStatus, MotionEvent};
use smithay::input::{Seat, SeatState};
-use smithay::output::{self, Output, PhysicalProperties, Subpixel};
+use smithay::output::{self, Output, OutputModeSource, PhysicalProperties, Subpixel};
use smithay::reexports::calloop::generic::Generic;
use smithay::reexports::calloop::timer::{TimeoutAction, Timer};
use smithay::reexports::calloop::{
@@ -53,6 +54,7 @@ use smithay::reexports::calloop::{
use smithay::reexports::wayland_protocols::ext::session_lock::v1::server::ext_session_lock_v1::ExtSessionLockV1;
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::WmCapabilities;
use smithay::reexports::wayland_protocols_misc::server_decoration as _server_decoration;
+use smithay::reexports::wayland_protocols_wlr::screencopy::v1::server::zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1;
use smithay::reexports::wayland_server::backend::{
ClientData, ClientId, DisconnectReason, GlobalId,
};
@@ -116,7 +118,7 @@ use crate::layout::{Layout, LayoutElement as _, MonitorRenderElement};
use crate::protocols::foreign_toplevel::{self, ForeignToplevelManagerState};
use crate::protocols::gamma_control::GammaControlManagerState;
use crate::protocols::output_management::OutputManagementManagerState;
-use crate::protocols::screencopy::{Screencopy, ScreencopyManagerState};
+use crate::protocols::screencopy::{Screencopy, ScreencopyBuffer, ScreencopyManagerState};
use crate::pw_utils::{Cast, PipeWire};
#[cfg(feature = "xdp-gnome-screencast")]
use crate::pw_utils::{CastSizeChange, CastTarget, PwToNiri};
@@ -125,8 +127,8 @@ use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;
use crate::render_helpers::renderer::NiriRenderer;
use crate::render_helpers::texture::TextureBuffer;
use crate::render_helpers::{
- render_to_encompassing_texture, render_to_shm, render_to_texture, render_to_vec, shaders,
- RenderTarget,
+ render_to_dmabuf, render_to_encompassing_texture, render_to_shm, render_to_texture,
+ render_to_vec, shaders, RenderTarget,
};
use crate::ui::config_error_notification::ConfigErrorNotification;
use crate::ui::exit_confirm_dialog::ExitConfirmDialog;
@@ -2041,6 +2043,8 @@ impl Niri {
#[cfg(feature = "xdp-gnome-screencast")]
self.stop_casts_for_target(CastTarget::Output(output.downgrade()));
+ self.remove_screencopy_output(output);
+
// Disable the output global and remove some time later to give the clients some time to
// process it.
let global = state.global;
@@ -3091,16 +3095,20 @@ impl Niri {
// However, this should probably be restricted to sending frame callbacks to more surfaces,
// to err on the safe side.
self.send_frame_callbacks(output);
-
- #[cfg(feature = "xdp-gnome-screencast")]
backend.with_primary_renderer(|renderer| {
- // Render and send to PipeWire screencast streams.
- self.render_for_screen_cast(renderer, output, target_presentation_time);
+ #[cfg(feature = "xdp-gnome-screencast")]
+ {
+ // Render and send to PipeWire screencast streams.
+ self.render_for_screen_cast(renderer, output, target_presentation_time);
+
+ // FIXME: when a window is hidden, it should probably still receive frame callbacks
+ // and get rendered for screen cast. This is currently
+ // unimplemented, but happens to work by chance, since output
+ // redrawing is more eager than it should be.
+ self.render_windows_for_screen_cast(renderer, output, target_presentation_time);
+ }
- // FIXME: when a window is hidden, it should probably still receive frame callbacks and
- // get rendered for screen cast. This is currently unimplemented, but happens to work
- // by chance, since output redrawing is more eager than it should be.
- self.render_windows_for_screen_cast(renderer, output, target_presentation_time);
+ self.render_for_screencopy_with_damage(renderer, output);
});
}
@@ -3736,47 +3744,163 @@ impl Niri {
}
}
- pub fn render_for_screencopy(
+ pub fn render_for_screencopy_with_damage(
&mut self,
- backend: &mut Backend,
+ renderer: &mut GlesRenderer,
+ output: &Output,
+ ) {
+ let _span = tracy_client::span!("Niri::render_for_screencopy_with_damage");
+
+ let mut screencopy_state = mem::take(&mut self.screencopy_state);
+ let elements = OnceCell::new();
+
+ for queue in screencopy_state.queues_mut() {
+ let (damage_tracker, screencopy) = queue.split();
+ if let Some(screencopy) = screencopy {
+ if screencopy.output() == output {
+ let elements = elements.get_or_init(|| {
+ self.render(renderer, output, true, RenderTarget::ScreenCapture)
+ });
+ // FIXME: skip elements if not including pointers
+ let render_result = Self::render_for_screencopy_internal(
+ renderer,
+ output,
+ elements,
+ true,
+ damage_tracker,
+ screencopy,
+ );
+ match render_result {
+ Ok(damages) => {
+ if let Some(damages) = damages {
+ screencopy.damage(damages);
+ queue.pop().submit(false);
+ } else {
+ trace!("no damage found, waiting till next redraw");
+ }
+ }
+ Err(err) => {
+ // Recreate damage tracker to report full damage next check.
+ *damage_tracker =
+ OutputDamageTracker::new((0, 0), 1.0, Transform::Normal);
+ queue.pop();
+ warn!("error rendering for screencopy: {err:?}");
+ }
+ }
+ };
+ }
+ }
+
+ self.screencopy_state = screencopy_state;
+ }
+
+ pub fn render_for_screencopy_without_damage(
+ &mut self,
+ renderer: &mut GlesRenderer,
+ manager: &ZwlrScreencopyManagerV1,
screencopy: Screencopy,
) -> anyhow::Result<()> {
- let output = screencopy.output().clone();
- ensure!(self.output_state.contains_key(&output), "output is missing");
+ let _span = tracy_client::span!("Niri::render_for_screencopy");
- self.layout.update_render_elements(&output);
+ let output = screencopy.output();
+ ensure!(
+ self.output_state.contains_key(output),
+ "screencopy output missing"
+ );
- backend
- .with_primary_renderer(move |renderer| {
- let elements = self
- .render(
- renderer,
- &output,
- screencopy.overlay_cursor(),
- RenderTarget::ScreenCapture,
- )
- .into_iter()
- .rev();
-
- let region_loc = screencopy.region_loc();
- let elements = elements.map(|element| {
- RelocateRenderElement::from_element(
- element,
- region_loc.upscale(-1),
- Relocate::Relative,
- )
- });
+ self.layout.update_render_elements(output);
- let scale = output.current_scale().fractional_scale().into();
- let transform = output.current_transform();
- render_to_shm(renderer, screencopy.buffer(), scale, transform, elements)
- .context("error rendering to screencopy shm buffer: {err:?}")?;
+ let elements = self.render(
+ renderer,
+ output,
+ screencopy.overlay_cursor(),
+ RenderTarget::ScreenCapture,
+ );
+ let Some(queue) = self.screencopy_state.get_queue_mut(manager) else {
+ bail!("screencopy manager destroyed already");
+ };
+ let damage_tracker = queue.split().0;
+
+ let render_result = Self::render_for_screencopy_internal(
+ renderer,
+ output,
+ &elements,
+ false,
+ damage_tracker,
+ &screencopy,
+ )
+ .map(|_damage| ());
+
+ match render_result {
+ Ok(()) => screencopy.submit(false),
+ Err(_) => {
+ // Recreate damage tracker to report full damage next check.
+ *damage_tracker = OutputDamageTracker::new((0, 0), 1.0, Transform::Normal);
+ }
+ }
+ render_result
+ }
+
+ fn render_for_screencopy_internal<'a>(
+ renderer: &mut GlesRenderer,
+ output: &Output,
+ elements: &[OutputRenderElements<GlesRenderer>],
+ with_damage: bool,
+ damage_tracker: &'a mut OutputDamageTracker,
+ screencopy: &Screencopy,
+ ) -> anyhow::Result<Option<&'a Vec<Rectangle<i32, Physical>>>> {
+ let OutputModeSource::Static {
+ size: last_size,
+ scale: last_scale,
+ transform: last_transform,
+ } = damage_tracker.mode().clone()
+ else {
+ unreachable!("damage tracker must have static mode");
+ };
- screencopy.submit(false);
+ let size = screencopy.buffer_size();
+ let scale: Scale<f64> = output.current_scale().fractional_scale().into();
+ let transform = output.current_transform();
+
+ if size != last_size || scale != last_scale || transform != last_transform {
+ *damage_tracker = OutputDamageTracker::new(size, scale, transform);
+ }
- Ok(())
+ let region_loc = screencopy.region_loc();
+ let elements = elements
+ .iter()
+ .map(|element| {
+ RelocateRenderElement::from_element(
+ element,
+ region_loc.upscale(-1),
+ Relocate::Relative,
+ )
})
- .context("primary renderer is missing")?
+ .collect::<Vec<_>>();
+
+ // Just checked damage tracker has static mode
+ let damages = damage_tracker.damage_output(1, &elements).unwrap().0;
+ if with_damage && damages.is_none() {
+ return Ok(None);
+ }
+
+ let elements = elements.iter().rev();
+
+ match screencopy.buffer() {
+ ScreencopyBuffer::Dmabuf(dmabuf) => {
+ let _sync =
+ render_to_dmabuf(renderer, dmabuf.clone(), size, scale, transform, elements)
+ .context("error rendering to screencopy dmabuf")?;
+ }
+ ScreencopyBuffer::Shm(wl_buffer) => {
+ render_to_shm(renderer, wl_buffer, size, scale, transform, elements)
+ .context("error rendering to screencopy shm buffer")?;
+ }
+ }
+ if let Err(err) = renderer.unbind() {
+ warn!("error unbinding after rendering for screencopy: {err:?}");
+ }
+ Ok(damages)
}
#[cfg(feature = "xdp-gnome-screencast")]
@@ -3829,6 +3953,13 @@ impl Niri {
}
}
+ pub fn remove_screencopy_output(&mut self, output: &Output) {
+ let _span = tracy_client::span!("Niri::remove_screencopy_output");
+ for queue in self.screencopy_state.queues_mut() {
+ queue.remove_output(output);
+ }
+ }
+
pub fn debug_toggle_damage(&mut self) {
self.debug_draw_damage = !self.debug_draw_damage;
diff --git a/src/protocols/screencopy.rs b/src/protocols/screencopy.rs
index fd6bda68..74c891d0 100644
--- a/src/protocols/screencopy.rs
+++ b/src/protocols/screencopy.rs
@@ -1,7 +1,11 @@
+use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::UNIX_EPOCH;
+use smithay::backend::allocator::dmabuf::Dmabuf;
+use smithay::backend::allocator::{Buffer, Fourcc};
+use smithay::backend::renderer::damage::OutputDamageTracker;
use smithay::output::Output;
use smithay::reexports::wayland_protocols_wlr::screencopy::v1::server::zwlr_screencopy_frame_v1::{
Flags, ZwlrScreencopyFrameV1,
@@ -11,17 +15,60 @@ use smithay::reexports::wayland_protocols_wlr::screencopy::v1::server::{
zwlr_screencopy_frame_v1, zwlr_screencopy_manager_v1,
};
use smithay::reexports::wayland_server::protocol::wl_buffer::WlBuffer;
-use smithay::reexports::wayland_server::protocol::wl_shm;
+use smithay::reexports::wayland_server::protocol::wl_shm::Format;
use smithay::reexports::wayland_server::{
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,
};
-use smithay::utils::{Physical, Point, Rectangle, Size};
-use smithay::wayland::shm;
+use smithay::utils::{Physical, Point, Rectangle, Size, Transform};
+use smithay::wayland::{dmabuf, shm};
-// We do not support copy_with_damage() semantics yet.
-const VERSION: u32 = 1;
+const VERSION: u32 = 3;
-pub struct ScreencopyManagerState;
+pub struct ScreencopyQueue {
+ damage_tracker: OutputDamageTracker,
+ screencopies: Vec<Screencopy>,
+}
+
+impl Default for ScreencopyQueue {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl ScreencopyQueue {
+ pub fn new() -> Self {
+ Self {
+ damage_tracker: OutputDamageTracker::new((0, 0), 1.0, Transform::Normal),
+ screencopies: Vec::new(),
+ }
+ }
+
+ pub fn split(&mut self) -> (&mut OutputDamageTracker, Option<&Screencopy>) {
+ let ScreencopyQueue {
+ damage_tracker,
+ screencopies,
+ } = self;
+ (damage_tracker, screencopies.first())
+ }
+
+ pub fn push(&mut self, screencopy: Screencopy) {
+ self.screencopies.push(screencopy);
+ }
+
+ pub fn pop(&mut self) -> Screencopy {
+ self.screencopies.pop().unwrap()
+ }
+
+ pub fn remove_output(&mut self, output: &Output) {
+ self.screencopies
+ .retain(|screencopy| screencopy.output() != output);
+ }
+}
+
+#[derive(Default)]
+pub struct ScreencopyManagerState {
+ queues: HashMap<ZwlrScreencopyManagerV1, ScreencopyQueue>,
+}
pub struct ScreencopyManagerGlobalData {
filter: Box<dyn for<'c> Fn(&'c Client) -> bool + Send + Sync>,
@@ -42,7 +89,28 @@ impl ScreencopyManagerState {
};
display.create_global::<D, ZwlrScreencopyManagerV1, _>(VERSION, global_data);
- Self
+ Self {
+ queues: HashMap::new(),
+ }
+ }
+
+ pub fn bind(&mut self, manager: &ZwlrScreencopyManagerV1) {
+ // Clean up all entries if its manager is dead and its queue is empty.
+ self.queues
+ .retain(|k, v| k.is_alive() || !v.screencopies.is_empty());
+
+ self.queues.insert(manager.clone(), ScreencopyQueue::new());
+ }
+
+ pub fn get_queue_mut(
+ &mut self,
+ manager: &ZwlrScreencopyManagerV1,
+ ) -> Option<&mut ScreencopyQueue> {
+ self.queues.get_mut(manager)
+ }
+
+ pub fn queues_mut(&mut self) -> impl Iterator<Item = &mut ScreencopyQueue> {
+ self.queues.values_mut()
}
}
@@ -56,14 +124,15 @@ where
D: 'static,
{
fn bind(
- _state: &mut D,
+ state: &mut D,
_display: &DisplayHandle,
_client: &Client,
manager: New<ZwlrScreencopyManagerV1>,
_manager_state: &ScreencopyManagerGlobalData,
data_init: &mut DataInit<'_, D>,
) {
- data_init.init(manager, ());
+ let manager = data_init.init(manager, ());
+ state.screencopy_state().bind(&manager);
}
fn can_view(client: Client, global_data: &ScreencopyManagerGlobalData) -> bool {
@@ -82,7 +151,7 @@ where
fn request(
_state: &mut D,
_client: &Client,
- _manager: &ZwlrScreencopyManagerV1,
+ manager: &ZwlrScreencopyManagerV1,
request: zwlr_screencopy_manager_v1::Request,
_data: &(),
_display: &DisplayHandle,
@@ -174,6 +243,7 @@ where
let frame = data_init.init(
frame,
ScreencopyFrameState::Pending {
+ manager: manager.clone(),
info,
copied: Arc::new(AtomicBool::new(false)),
},
@@ -181,30 +251,31 @@ where
// Send desired SHM buffer parameters.
frame.buffer(
- wl_shm::Format::Argb8888,
+ Format::Xrgb8888,
buffer_size.w as u32,
buffer_size.h as u32,
buffer_size.w as u32 * 4,
);
- // if manager.version() >= 3 {
- // // Send desired DMA buffer parameters.
- // frame.linux_dmabuf(
- // Fourcc::Argb8888 as u32,
- // buffer_size.w as u32,
- // buffer_size.h as u32,
- // );
- //
- // // Notify client that all supported buffers were enumerated.
- // frame.buffer_done();
- // }
+ if frame.version() >= 3 {
+ // Send desired DMA buffer parameters.
+ frame.linux_dmabuf(
+ Fourcc::Xrgb8888 as u32,
+ buffer_size.w as u32,
+ buffer_size.h as u32,
+ );
+
+ // Notify client that all supported buffers were enumerated.
+ frame.buffer_done();
+ }
}
}
/// Handler trait for wlr-screencopy.
pub trait ScreencopyHandler {
/// Handle new screencopy request.
- fn frame(&mut self, frame: Screencopy);
+ fn frame(&mut self, manager: &ZwlrScreencopyManagerV1, screencopy: Screencopy);
+ fn screencopy_state(&mut self) -> &mut ScreencopyManagerState;
}
#[allow(missing_docs)]
@@ -236,6 +307,7 @@ pub struct ScreencopyFrameInfo {
pub enum ScreencopyFrameState {
Failed,
Pending {
+ manager: ZwlrScreencopyManagerV1,
info: ScreencopyFrameInfo,
copied: Arc<AtomicBool>,
},
@@ -260,9 +332,13 @@ where
return;
}
- let (info, copied) = match data {
- ScreencopyFrameState::Failed => return,
- ScreencopyFrameState::Pending { info, copied } => (info, copied),
+ let ScreencopyFrameState::Pending {
+ manager,
+ info,
+ copied,
+ } = data
+ else {
+ return;
};
if copied.load(Ordering::SeqCst) {
@@ -275,44 +351,71 @@ where
let (buffer, with_damage) = match request {
zwlr_screencopy_frame_v1::Request::Copy { buffer } => (buffer, false),
- // zwlr_screencopy_frame_v1::Request::CopyWithDamage { buffer } => (buffer, true),
+ zwlr_screencopy_frame_v1::Request::CopyWithDamage { buffer } => (buffer, true),
_ => unreachable!(),
};
- if !shm::with_buffer_contents(&buffer, |_buf, shm_len, buffer_data| {
- buffer_data.format == wl_shm::Format::Argb8888
- && buffer_data.stride == info.buffer_size.w * 4
- && buffer_data.height == info.buffer_size.h
- && shm_len as i32 == buffer_data.stride * buffer_data.height
+ let size = info.buffer_size;
+
+ let buffer = if let Ok(dmabuf) = dmabuf::get_dmabuf(&buffer) {
+ if dmabuf.format().code == Fourcc::Xrgb8888
+ && dmabuf.width() == size.w as u32
+ && dmabuf.height() == size.h as u32
+ {
+ ScreencopyBuffer::Dmabuf(dmabuf.clone())
+ } else {
+ frame.post_error(
+ zwlr_screencopy_frame_v1::Error::InvalidBuffer,
+ "invalid dmabuf parameters",
+ );
+ return;
+ }
+ } else if shm::with_buffer_contents(&buffer, |_, shm_len, buffer_data| {
+ buffer_data.format == Format::Xrgb8888
+ && buffer_data.width == size.w
+ && buffer_data.height == size.h
+ && buffer_data.stride == size.w * 4
+ && shm_len == buffer_data.stride as usize * buffer_data.height as usize
})
.unwrap_or(false)
{
+ ScreencopyBuffer::Shm(buffer)
+ } else {
frame.post_error(
zwlr_screencopy_frame_v1::Error::InvalidBuffer,
"invalid buffer",
);
return;
- }
+ };
copied.store(true, Ordering::SeqCst);
- state.frame(Screencopy {
- with_damage,
- buffer,
- frame: frame.clone(),
- info: info.clone(),
- submitted: false,
- });
+ state.frame(
+ manager,
+ Screencopy {
+ buffer,
+ frame: frame.clone(),
+ info: info.clone(),
+ with_damage,
+ submitted: false,
+ },
+ );
}
}
+/// Screencopy buffer.
+#[derive(Clone)]
+pub enum ScreencopyBuffer {
+ Dmabuf(Dmabuf),
+ Shm(WlBuffer),
+}
+
/// Screencopy frame.
pub struct Screencopy {
info: ScreencopyFrameInfo,
frame: ZwlrScreencopyFrameV1,
- #[allow(unused)]
+ buffer: ScreencopyBuffer,
with_damage: bool,
- buffer: WlBuffer,
submitted: bool,
}
@@ -326,7 +429,7 @@ impl Drop for Screencopy {
impl Screencopy {
/// Get the target buffer to copy to.
- pub fn buffer(&self) -> &WlBuffer {
+ pub fn buffer(&self) -> &ScreencopyBuffer {
&self.buffer
}
@@ -346,14 +449,16 @@ impl Screencopy {
self.info.overlay_cursor
}
- // pub fn damage(&mut self, damage: &[Rectangle<i32, Physical>]) {
- // assert!(self.with_damage);
- //
- // for Rectangle { loc, size } in damage {
- // self.frame
- // .damage(loc.x as u32, loc.y as u32, size.w as u32, size.h as u32);
- // }
- // }
+ pub fn with_damage(&self) -> bool {
+ self.with_damage
+ }
+
+ pub fn damage(&self, damages: &[Rectangle<i32, Physical>]) {
+ for Rectangle { loc, size } in damages {
+ self.frame
+ .damage(loc.x as u32, loc.y as u32, size.w as u32, size.h as u32);
+ }
+ }
/// Submit the copied content.
pub fn submit(mut self, y_invert: bool) {
@@ -374,25 +479,4 @@ impl Screencopy {
// Mark frame as submitted to ensure destructor isn't run.
self.submitted = true;
}
-
- // pub fn submit_after_sync<T>(
- // self,
- // y_invert: bool,
- // sync_point: Option<OwnedFd>,
- // event_loop: &LoopHandle<'_, T>,
- // ) {
- // match sync_point {
- // None => self.submit(y_invert),
- // Some(sync_fd) => {
- // let source = Generic::new(sync_fd, Interest::READ, Mode::OneShot);
- // let mut screencopy = Some(self);
- // event_loop
- // .insert_source(source, move |_, _, _| {
- // screencopy.take().unwrap().submit(y_invert);
- // Ok(PostAction::Remove)
- // })
- // .unwrap();
- // }
- // }
- // }
}
diff --git a/src/render_helpers/mod.rs b/src/render_helpers/mod.rs
index 12ebd024..f69eec05 100644
--- a/src/render_helpers/mod.rs
+++ b/src/render_helpers/mod.rs
@@ -2,12 +2,13 @@ use std::ptr;
use anyhow::{ensure, Context};
use niri_config::BlockOutFrom;
-use smithay::backend::allocator::Fourcc;
+use smithay::backend::allocator::dmabuf::Dmabuf;
+use smithay::backend::allocator::{Buffer, Fourcc};
use smithay::backend::renderer::element::utils::{Relocate, RelocateRenderElement};
use smithay::backend::renderer::element::{Kind, RenderElement};
use smithay::backend::renderer::gles::{GlesMapping, GlesRenderer, GlesTexture};
use smithay::backend::renderer::sync::SyncPoint;
-use smithay::backend::renderer::{buffer_dimensions, Bind, ExportMem, Frame, Offscreen, Renderer};
+use smithay::backend::renderer::{Bind, ExportMem, Frame, Offscreen, Renderer};
use smithay::reexports::wayland_server::protocol::wl_buffer::WlBuffer;
use smithay::reexports::wayland_server::protocol::wl_shm;
use smithay::utils::{Logical, Physical, Point, Rectangle, Scale, Size, Transform};
@@ -233,16 +234,19 @@ pub fn render_to_vec(
Ok(copy.to_vec())
}
-#[cfg(feature = "xdp-gnome-screencast")]
pub fn render_to_dmabuf(
renderer: &mut GlesRenderer,
- dmabuf: smithay::backend::allocator::dmabuf::Dmabuf,
+ dmabuf: Dmabuf,
size: Size<i32, Physical>,
scale: Scale<f64>,
transform: Transform,
elements: impl Iterator<Item = impl RenderElement<GlesRenderer>>,
) -> anyhow::Result<SyncPoint> {
let _span = tracy_client::span!();
+ ensure!(
+ dmabuf.width() == size.w as u32 && dmabuf.height() == size.h as u32,
+ "invalid buffer size"
+ );
renderer.bind(dmabuf).context("error binding texture")?;
render_elements(renderer, size, scale, transform, elements)
}
@@ -250,32 +254,28 @@ pub fn render_to_dmabuf(
pub fn render_to_shm(
renderer: &mut GlesRenderer,
buffer: &WlBuffer,
+ size: Size<i32, Physical>,
scale: Scale<f64>,
transform: Transform,
elements: impl Iterator<Item = impl RenderElement<GlesRenderer>>,
) -> anyhow::Result<()> {
let _span = tracy_client::span!();
-
- let buffer_size = buffer_dimensions(buffer).context("error getting buffer dimensions")?;
- let size = buffer_size.to_logical(1, Transform::Normal).to_physical(1);
-
- let mapping =
- render_and_download(renderer, size, scale, transform, Fourcc::Argb8888, elements)?;
- let bytes = renderer
- .map_texture(&mapping)
- .context("error mapping texture")?;
-
shm::with_buffer_contents_mut(buffer, |shm_buffer, shm_len, buffer_data| {
ensure!(
// The buffer prefers pixels in little endian ...
- buffer_data.format == wl_shm::Format::Argb8888
- && buffer_data.stride == size.w * 4
+ buffer_data.format == wl_shm::Format::Xrgb8888
+ && buffer_data.width == size.w
&& buffer_data.height == size.h
- && shm_len as i32 == buffer_data.stride * buffer_data.height,
+ && buffer_data.stride == size.w * 4
+ && shm_len == buffer_data.stride as usize * buffer_data.height as usize,
"invalid buffer format or size"
);
+ let mapping =
+ render_and_download(renderer, size, scale, transform, Fourcc::Xrgb8888, elements)?;
- ensure!(bytes.len() == shm_len, "mapped buffer has wrong length");
+ let bytes = renderer
+ .map_texture(&mapping)
+ .context("error mapping texture")?;
unsafe {
let _span = tracy_client::span!("copy_nonoverlapping");