mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-24 02:01:18 +07:00
ipc: Add screencopy cast tracking
Track wlr-screencopy sessions that use with_damage as screencasts. These are used by tools like wl-screenrec for continuous recording.
This commit is contained in:
+33
-3
@@ -798,7 +798,6 @@ impl State {
|
|||||||
server.send_event(event);
|
server.send_event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "xdp-gnome-screencast")]
|
|
||||||
pub fn ipc_refresh_casts(&mut self) {
|
pub fn ipc_refresh_casts(&mut self) {
|
||||||
let Some(server) = &self.niri.ipc_server else {
|
let Some(server) = &self.niri.ipc_server else {
|
||||||
return;
|
return;
|
||||||
@@ -812,13 +811,16 @@ impl State {
|
|||||||
let mut events = Vec::new();
|
let mut events = Vec::new();
|
||||||
let mut seen = HashSet::new();
|
let mut seen = HashSet::new();
|
||||||
|
|
||||||
|
// Check PipeWire screencasts.
|
||||||
|
#[cfg(feature = "xdp-gnome-screencast")]
|
||||||
|
{
|
||||||
// Check pending dynamic casts.
|
// Check pending dynamic casts.
|
||||||
for pending in &self.niri.casting.pending_dynamic_casts {
|
for pending in &self.niri.casting.pending_dynamic_casts {
|
||||||
let stream_id = pending.stream_id.get();
|
let stream_id = pending.stream_id.get();
|
||||||
seen.insert(stream_id);
|
seen.insert(stream_id);
|
||||||
|
|
||||||
// Pending dynamic casts don't change any properties, so we only need to check if it's
|
// Pending dynamic casts don't change any properties, so we only need to check if
|
||||||
// missing from the state.
|
// it's missing from the state.
|
||||||
if !state.casts.contains_key(&stream_id) {
|
if !state.casts.contains_key(&stream_id) {
|
||||||
let cast = niri_ipc::Cast {
|
let cast = niri_ipc::Cast {
|
||||||
session_id: pending.session_id.get(),
|
session_id: pending.session_id.get(),
|
||||||
@@ -850,6 +852,34 @@ impl State {
|
|||||||
events.push(Event::CastStartedOrChanged { cast });
|
events.push(Event::CastStartedOrChanged { cast });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check screencopy casts.
|
||||||
|
for queue in self.niri.screencopy_state.queues() {
|
||||||
|
if let Some(cast_info) = queue.cast() {
|
||||||
|
let stream_id = cast_info.stream_id.get();
|
||||||
|
seen.insert(stream_id);
|
||||||
|
|
||||||
|
if state.casts.get(&stream_id).is_none_or(|existing| {
|
||||||
|
// Only this property can change.
|
||||||
|
match &existing.target {
|
||||||
|
niri_ipc::CastTarget::Output { name } => *name != cast_info.output_name,
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
let cast = niri_ipc::Cast {
|
||||||
|
session_id: cast_info.session_id.get(),
|
||||||
|
stream_id,
|
||||||
|
target: niri_ipc::CastTarget::Output {
|
||||||
|
name: cast_info.output_name.clone(),
|
||||||
|
},
|
||||||
|
is_dynamic_target: false,
|
||||||
|
is_active: true,
|
||||||
|
};
|
||||||
|
events.push(Event::CastStartedOrChanged { cast });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check for stopped casts.
|
// Check for stopped casts.
|
||||||
for stream_id in state.casts.keys() {
|
for stream_id in state.casts.keys() {
|
||||||
|
|||||||
@@ -816,7 +816,6 @@ impl State {
|
|||||||
// screencasts.
|
// screencasts.
|
||||||
#[cfg(feature = "xdp-gnome-screencast")]
|
#[cfg(feature = "xdp-gnome-screencast")]
|
||||||
self.niri.refresh_mapped_cast_window_rules();
|
self.niri.refresh_mapped_cast_window_rules();
|
||||||
#[cfg(feature = "xdp-gnome-screencast")]
|
|
||||||
self.ipc_refresh_casts();
|
self.ipc_refresh_casts();
|
||||||
|
|
||||||
self.niri.refresh_window_rules();
|
self.niri.refresh_window_rules();
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use smithay::backend::allocator::dmabuf::Dmabuf;
|
|||||||
use smithay::backend::allocator::{Buffer, Fourcc};
|
use smithay::backend::allocator::{Buffer, Fourcc};
|
||||||
use smithay::backend::renderer::damage::OutputDamageTracker;
|
use smithay::backend::renderer::damage::OutputDamageTracker;
|
||||||
use smithay::backend::renderer::sync::SyncPoint;
|
use smithay::backend::renderer::sync::SyncPoint;
|
||||||
use smithay::output::Output;
|
use smithay::output::{Output, WeakOutput};
|
||||||
use smithay::reexports::wayland_protocols_wlr::screencopy::v1::server::{
|
use smithay::reexports::wayland_protocols_wlr::screencopy::v1::server::{
|
||||||
zwlr_screencopy_frame_v1, zwlr_screencopy_manager_v1,
|
zwlr_screencopy_frame_v1, zwlr_screencopy_manager_v1,
|
||||||
};
|
};
|
||||||
@@ -23,7 +23,7 @@ use smithay::wayland::{dmabuf, shm};
|
|||||||
use zwlr_screencopy_frame_v1::{Flags, ZwlrScreencopyFrameV1};
|
use zwlr_screencopy_frame_v1::{Flags, ZwlrScreencopyFrameV1};
|
||||||
use zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1;
|
use zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1;
|
||||||
|
|
||||||
use crate::utils::get_monotonic_time;
|
use crate::utils::{get_monotonic_time, CastSessionId, CastStreamId};
|
||||||
|
|
||||||
const VERSION: u32 = 3;
|
const VERSION: u32 = 3;
|
||||||
|
|
||||||
@@ -33,6 +33,40 @@ pub struct ScreencopyQueue {
|
|||||||
pending_frames: HashSet<ZwlrScreencopyFrameV1>,
|
pending_frames: HashSet<ZwlrScreencopyFrameV1>,
|
||||||
/// Queue of screencopies waiting for a corresponding output redraw with damage.
|
/// Queue of screencopies waiting for a corresponding output redraw with damage.
|
||||||
screencopies: Vec<Screencopy>,
|
screencopies: Vec<Screencopy>,
|
||||||
|
/// Cast tracking, set when the first with_damage request arrives.
|
||||||
|
cast: Option<ScreencopyCast>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ScreencopyCast {
|
||||||
|
pub session_id: CastSessionId,
|
||||||
|
pub stream_id: CastStreamId,
|
||||||
|
/// Output being captured.
|
||||||
|
///
|
||||||
|
/// Generally equal to the front entry in the queue, and persisted here when the queue becomes
|
||||||
|
/// empty.
|
||||||
|
pub output: WeakOutput,
|
||||||
|
/// Cached name of the output.
|
||||||
|
pub output_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScreencopyCast {
|
||||||
|
fn new(output: &Output) -> Self {
|
||||||
|
Self {
|
||||||
|
session_id: CastSessionId::next(),
|
||||||
|
stream_id: CastStreamId::next(),
|
||||||
|
output: output.downgrade(),
|
||||||
|
output_name: output.name(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_output(&mut self, output: &Output) {
|
||||||
|
// Only allocate a new name when the output differs.
|
||||||
|
let weak = output.downgrade();
|
||||||
|
if self.output != weak {
|
||||||
|
self.output = weak;
|
||||||
|
self.output_name = output.name();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ScreencopyQueue {
|
impl Default for ScreencopyQueue {
|
||||||
@@ -47,6 +81,7 @@ impl ScreencopyQueue {
|
|||||||
damage_tracker: OutputDamageTracker::new((0, 0), 1.0, Transform::Normal),
|
damage_tracker: OutputDamageTracker::new((0, 0), 1.0, Transform::Normal),
|
||||||
pending_frames: HashSet::new(),
|
pending_frames: HashSet::new(),
|
||||||
screencopies: Vec::new(),
|
screencopies: Vec::new(),
|
||||||
|
cast: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +89,11 @@ impl ScreencopyQueue {
|
|||||||
self.pending_frames.is_empty() && self.screencopies.is_empty()
|
self.pending_frames.is_empty() && self.screencopies.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the cast tracking info, if this queue is tracking a cast.
|
||||||
|
pub fn cast(&self) -> Option<&ScreencopyCast> {
|
||||||
|
self.cast.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn split(&mut self) -> (&mut OutputDamageTracker, Option<&Screencopy>) {
|
pub fn split(&mut self) -> (&mut OutputDamageTracker, Option<&Screencopy>) {
|
||||||
let ScreencopyQueue {
|
let ScreencopyQueue {
|
||||||
damage_tracker,
|
damage_tracker,
|
||||||
@@ -69,11 +109,30 @@ impl ScreencopyQueue {
|
|||||||
error!("only screencopy with damage can be pushed in the queue");
|
error!("only screencopy with damage can be pushed in the queue");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(cast) = &mut self.cast {
|
||||||
|
// Update cast output when pushing a new front screencopy.
|
||||||
|
if self.screencopies.is_empty() {
|
||||||
|
cast.update_output(screencopy.output());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// First with_damage request, mark this as a screencast.
|
||||||
|
let output = screencopy.output();
|
||||||
|
self.cast = Some(ScreencopyCast::new(output));
|
||||||
|
}
|
||||||
|
|
||||||
self.screencopies.push(screencopy);
|
self.screencopies.push(screencopy);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop(&mut self) -> Screencopy {
|
pub fn pop(&mut self) -> Screencopy {
|
||||||
self.screencopies.remove(0)
|
let rv = self.screencopies.remove(0);
|
||||||
|
|
||||||
|
// Update cast output (most of the time we expect this to be the same).
|
||||||
|
if let Some(first) = self.screencopies.first() {
|
||||||
|
let cast = self.cast.as_mut().unwrap();
|
||||||
|
cast.update_output(first.output());
|
||||||
|
}
|
||||||
|
|
||||||
|
rv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,6 +194,10 @@ impl ScreencopyManagerState {
|
|||||||
self.cleanup_queues();
|
self.cleanup_queues();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn queues(&self) -> impl Iterator<Item = &ScreencopyQueue> {
|
||||||
|
self.queues.values()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn with_queues_mut(&mut self, mut f: impl FnMut(&mut ScreencopyQueue)) {
|
pub fn with_queues_mut(&mut self, mut f: impl FnMut(&mut ScreencopyQueue)) {
|
||||||
for queue in self.queues.values_mut() {
|
for queue in self.queues.values_mut() {
|
||||||
f(queue);
|
f(queue);
|
||||||
|
|||||||
Reference in New Issue
Block a user