Skip to content

Commit

Permalink
Implement wlr-screencopy v1 (YaLTeR#243)
Browse files Browse the repository at this point in the history
* Implement wlr-screencopy

* Finish the implementation

Lots of changes, mainly to fix transform handling. Turns out, grim
expects transformed buffers and untransforms them by itself using info
from wl_output. This means that render helpers needed to learn how to
actually render transformed buffers.

Also, it meant that y_invert is no longer needed.

Next, moved the rendering to the Screencopy frame handler. Turns out,
copy() is more or less expected to return immediately, whereas
copy_with_damage() is expected to wait until the next VBlank. At least
that's the intent I parse reading the protocol.

Finally, brought the version from 3 down to 1, because
copy_with_damage() will need bigger changes. Grim still works, others
not really, mainly because they bind v3 unnecessarily, even if they
don't use the damage request.

---------

Co-authored-by: Ivan Molodetskikh <[email protected]>
  • Loading branch information
sodiboo and YaLTeR authored Mar 8, 2024
1 parent 1a784e6 commit ca22e70
Show file tree
Hide file tree
Showing 7 changed files with 566 additions and 63 deletions.
30 changes: 15 additions & 15 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 14 additions & 1 deletion src/handlers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,13 @@ use smithay::{
delegate_text_input_manager, delegate_virtual_keyboard_manager,
};

use crate::delegate_foreign_toplevel;
use crate::niri::{ClientState, State};
use crate::protocols::foreign_toplevel::{
self, ForeignToplevelHandler, ForeignToplevelManagerState,
};
use crate::protocols::screencopy::{Screencopy, ScreencopyHandler};
use crate::utils::output_size;
use crate::{delegate_foreign_toplevel, delegate_screencopy};

impl SeatHandler for State {
type KeyboardFocus = WlSurface;
Expand Down Expand Up @@ -380,6 +381,18 @@ 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:?}");
}
}
}
delegate_screencopy!(State);

impl DrmLeaseHandler for State {
fn drm_lease_state(&mut self, node: DrmNode) -> &mut DrmLeaseState {
&mut self
Expand Down
94 changes: 80 additions & 14 deletions src/niri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::Context;
use anyhow::{ensure, Context};
use calloop::futures::Scheduler;
use niri_config::{Config, TrackLayout};
use smithay::backend::allocator::Fourcc;
Expand All @@ -18,7 +18,9 @@ use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRen
use smithay::backend::renderer::element::surface::{
render_elements_from_surface_tree, WaylandSurfaceRenderElement,
};
use smithay::backend::renderer::element::utils::{select_dmabuf_feedback, RelocateRenderElement};
use smithay::backend::renderer::element::utils::{
select_dmabuf_feedback, Relocate, RelocateRenderElement,
};
use smithay::backend::renderer::element::{
default_primary_scanout_output_compare, AsRenderElements, Id, Kind, PrimaryScanoutOutput,
RenderElementStates,
Expand Down Expand Up @@ -97,9 +99,10 @@ use crate::input::{apply_libinput_settings, TabletData};
use crate::ipc::server::IpcServer;
use crate::layout::{Layout, MonitorRenderElement};
use crate::protocols::foreign_toplevel::{self, ForeignToplevelManagerState};
use crate::protocols::screencopy::{Screencopy, ScreencopyManagerState};
use crate::pw_utils::{Cast, PipeWire};
use crate::render_helpers::renderer::NiriRenderer;
use crate::render_helpers::{render_to_texture, render_to_vec};
use crate::render_helpers::{render_to_shm, render_to_texture, render_to_vec};
use crate::ui::config_error_notification::ConfigErrorNotification;
use crate::ui::exit_confirm_dialog::ExitConfirmDialog;
use crate::ui::hotkey_overlay::HotkeyOverlay;
Expand Down Expand Up @@ -154,6 +157,7 @@ pub struct Niri {
pub layer_shell_state: WlrLayerShellState,
pub session_lock_state: SessionLockManagerState,
pub foreign_toplevel_state: ForeignToplevelManagerState,
pub screencopy_state: ScreencopyManagerState,
pub shm_state: ShmState,
pub output_manager_state: OutputManagerState,
pub dmabuf_state: DmabufState,
Expand Down Expand Up @@ -910,6 +914,9 @@ impl Niri {
ForeignToplevelManagerState::new::<State, _>(&display_handle, |client| {
!client.get_data::<ClientState>().unwrap().restricted
});
let screencopy_state = ScreencopyManagerState::new::<State, _>(&display_handle, |client| {
!client.get_data::<ClientState>().unwrap().restricted
});

let mut seat: Seat<State> = seat_state.new_wl_seat(&display_handle, backend.seat_name());
seat.add_keyboard(
Expand Down Expand Up @@ -1030,6 +1037,7 @@ impl Niri {
layer_shell_state,
session_lock_state,
foreign_toplevel_state,
screencopy_state,
text_input_state,
input_method_state,
virtual_keyboard_state,
Expand Down Expand Up @@ -2166,13 +2174,11 @@ impl Niri {
// to err on the safe side.
self.send_frame_callbacks(output);

// Render and send to PipeWire screencast streams.
#[cfg(feature = "xdp-gnome-screencast")]
{
backend.with_primary_renderer(|renderer| {
self.render_for_screen_cast(renderer, output, target_presentation_time);
});
}
backend.with_primary_renderer(|renderer| {
// Render and send to PipeWire screencast streams.
#[cfg(feature = "xdp-gnome-screencast")]
self.render_for_screen_cast(renderer, output, target_presentation_time);
});
}

pub fn update_primary_scanout_output(
Expand Down Expand Up @@ -2586,7 +2592,9 @@ impl Niri {
.get_or_insert_with(|| self.render::<GlesRenderer>(renderer, output, true));
let elements = elements.iter().rev();

if let Err(err) = render_to_dmabuf(renderer, dmabuf, size, scale, elements) {
if let Err(err) =
render_to_dmabuf(renderer, dmabuf, size, scale, Transform::Normal, elements)
{
warn!("error rendering to dmabuf: {err:?}");
continue;
}
Expand All @@ -2606,6 +2614,42 @@ impl Niri {
}
}

pub fn render_for_screencopy(
&mut self,
backend: &mut Backend,
screencopy: Screencopy,
) -> anyhow::Result<()> {
let output = screencopy.output().clone();
ensure!(self.output_state.contains_key(&output), "output is missing");

backend
.with_primary_renderer(move |renderer| {
let elements = self
.render(renderer, &output, screencopy.overlay_cursor())
.into_iter()
.rev();

let region_loc = screencopy.region_loc();
let elements = elements.map(|element| {
RelocateRenderElement::from_element(
element,
region_loc.upscale(-1),
Relocate::Relative,
)
});

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:?}")?;

screencopy.submit(false);

Ok(())
})
.context("primary renderer is missing")?
}

#[cfg(feature = "xdp-gnome-screencast")]
fn stop_cast(&mut self, session_id: usize) {
let _span = tracy_client::span!("Niri::stop_cast");
Expand Down Expand Up @@ -2665,7 +2709,14 @@ impl Niri {
let elements = self.render::<GlesRenderer>(renderer, &output, true);
let elements = elements.iter().rev();

let res = render_to_texture(renderer, size, scale, Fourcc::Abgr8888, elements);
let res = render_to_texture(
renderer,
size,
scale,
Transform::Normal,
Fourcc::Abgr8888,
elements,
);
let screenshot = match res {
Ok((texture, _)) => texture,
Err(err) => {
Expand Down Expand Up @@ -2695,7 +2746,14 @@ impl Niri {
let scale = Scale::from(output.current_scale().fractional_scale());
let elements = self.render::<GlesRenderer>(renderer, output, true);
let elements = elements.iter().rev();
let pixels = render_to_vec(renderer, size, scale, Fourcc::Abgr8888, elements)?;
let pixels = render_to_vec(
renderer,
size,
scale,
Transform::Normal,
Fourcc::Abgr8888,
elements,
)?;

self.save_screenshot(size, pixels)
.context("error saving screenshot")
Expand All @@ -2721,7 +2779,14 @@ impl Niri {
1.,
);
let elements = elements.iter().rev();
let pixels = render_to_vec(renderer, size, scale, Fourcc::Abgr8888, elements)?;
let pixels = render_to_vec(
renderer,
size,
scale,
Transform::Normal,
Fourcc::Abgr8888,
elements,
)?;

self.save_screenshot(size, pixels)
.context("error saving screenshot")
Expand Down Expand Up @@ -2824,6 +2889,7 @@ impl Niri {
renderer,
size,
Scale::from(f64::from(output_scale)),
Transform::Normal,
Fourcc::Abgr8888,
elements,
)?;
Expand Down
1 change: 1 addition & 0 deletions src/protocols/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod foreign_toplevel;
pub mod screencopy;
Loading

0 comments on commit ca22e70

Please sign in to comment.