mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-21 02:01:55 +07:00
feat: update screencopy to version 3
This commit is contained in:
committed by
Ivan Molodetskikh
parent
1cf5cfce06
commit
b6a7b3e9e4
+24
-7
@@ -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);
|
||||
|
||||
|
||||
+176
-45
@@ -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();
|
||||
self.layout.update_render_elements(output);
|
||||
|
||||
let region_loc = screencopy.region_loc();
|
||||
let elements = elements.map(|element| {
|
||||
RelocateRenderElement::from_element(
|
||||
element,
|
||||
region_loc.upscale(-1),
|
||||
Relocate::Relative,
|
||||
)
|
||||
});
|
||||
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 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 render_result = Self::render_for_screencopy_internal(
|
||||
renderer,
|
||||
output,
|
||||
&elements,
|
||||
false,
|
||||
damage_tracker,
|
||||
&screencopy,
|
||||
)
|
||||
.map(|_damage| ());
|
||||
|
||||
screencopy.submit(false);
|
||||
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
|
||||
}
|
||||
|
||||
Ok(())
|
||||
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");
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
||||
+156
-72
@@ -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();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
+18
-18
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user