mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-21 02:01:55 +07:00
screencopy: Add timeout to casts considered stopped
Otherwise xdp-wlr never stops the cast after it first starts.
This commit is contained in:
@@ -857,6 +857,11 @@ impl State {
|
||||
}
|
||||
|
||||
// Check screencopy casts.
|
||||
//
|
||||
// First, clear expired casts. Ideally we'd have a deadline timer, but our 1 second frame
|
||||
// callback timer calls refresh regularly, so that's fine as is.
|
||||
self.niri.screencopy_state.clear_expired_casts();
|
||||
|
||||
for queue in self.niri.screencopy_state.queues() {
|
||||
if let Some(cast_info) = queue.cast() {
|
||||
let stream_id = cast_info.stream_id.get();
|
||||
|
||||
@@ -27,6 +27,13 @@ use crate::utils::{get_monotonic_time, CastSessionId, CastStreamId};
|
||||
|
||||
const VERSION: u32 = 3;
|
||||
|
||||
/// Inactivity timeout for considering a screencopy cast as stopped.
|
||||
///
|
||||
/// xdg-desktop-portal-wlr keeps the screencopy manager alive across casts, so there's no way to
|
||||
/// tell that a screencast had stopped. So we use a timeout: if no new with_damage frames are
|
||||
/// requested for this timeout, consider the screencast finished.
|
||||
const CAST_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
|
||||
pub struct ScreencopyQueue {
|
||||
damage_tracker: OutputDamageTracker,
|
||||
/// Frames waiting for the client to call copy or destroy.
|
||||
@@ -47,6 +54,8 @@ pub struct ScreencopyCast {
|
||||
pub output: WeakOutput,
|
||||
/// Cached name of the output.
|
||||
pub output_name: String,
|
||||
/// Deadline after which this cast is considered stopped if no new frames arrive.
|
||||
pub deadline: Duration,
|
||||
}
|
||||
|
||||
impl ScreencopyCast {
|
||||
@@ -56,9 +65,14 @@ impl ScreencopyCast {
|
||||
stream_id: CastStreamId::next(),
|
||||
output: output.downgrade(),
|
||||
output_name: output.name(),
|
||||
deadline: get_monotonic_time() + CAST_TIMEOUT,
|
||||
}
|
||||
}
|
||||
|
||||
fn update_deadline(&mut self) {
|
||||
self.deadline = get_monotonic_time() + CAST_TIMEOUT;
|
||||
}
|
||||
|
||||
fn update_output(&mut self, output: &Output) {
|
||||
// Only allocate a new name when the output differs.
|
||||
let weak = output.downgrade();
|
||||
@@ -126,14 +140,60 @@ impl ScreencopyQueue {
|
||||
pub fn pop(&mut self) -> Screencopy {
|
||||
let rv = self.screencopies.remove(0);
|
||||
|
||||
// Update cast output (most of the time we expect this to be the same).
|
||||
let cast = self.cast.as_mut().unwrap();
|
||||
if let Some(first) = self.screencopies.first() {
|
||||
let cast = self.cast.as_mut().unwrap();
|
||||
// Update cast output (most of the time we expect this to be the same).
|
||||
cast.update_output(first.output());
|
||||
} else {
|
||||
// Queue became empty, update deadline for considering the cast stopped.
|
||||
cast.update_deadline();
|
||||
}
|
||||
|
||||
rv
|
||||
}
|
||||
|
||||
pub fn clear_expired_cast(&mut self) {
|
||||
if let Some(cast) = &self.cast {
|
||||
// Check deadline if there are no in-flight frames.
|
||||
if self.screencopies.is_empty() && cast.deadline <= get_monotonic_time() {
|
||||
self.cast = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_output(&mut self, output: &Output) {
|
||||
if self.screencopies.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.screencopies
|
||||
.retain(|screencopy| screencopy.output() != output);
|
||||
|
||||
if let Some(cast) = &mut self.cast {
|
||||
if self.screencopies.is_empty() {
|
||||
// Queue became empty, update deadline for considering the cast stopped.
|
||||
cast.update_deadline();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_frame(&mut self, frame: &ZwlrScreencopyFrameV1) {
|
||||
self.pending_frames.remove(frame);
|
||||
|
||||
if self.screencopies.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.screencopies
|
||||
.retain(|screencopy| screencopy.frame != *frame);
|
||||
|
||||
if let Some(cast) = &mut self.cast {
|
||||
if self.screencopies.is_empty() {
|
||||
// Queue became empty, update deadline for considering the cast stopped.
|
||||
cast.update_deadline();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -186,9 +246,7 @@ impl ScreencopyManagerState {
|
||||
|
||||
pub fn remove_output(&mut self, output: &Output) {
|
||||
for queue in self.queues.values_mut() {
|
||||
queue
|
||||
.screencopies
|
||||
.retain(|screencopy| screencopy.output() != output);
|
||||
queue.remove_output(output);
|
||||
}
|
||||
|
||||
self.cleanup_queues();
|
||||
@@ -210,6 +268,12 @@ impl ScreencopyManagerState {
|
||||
self.queues
|
||||
.retain(|manager, queue| manager.is_alive() || !queue.is_empty());
|
||||
}
|
||||
|
||||
pub fn clear_expired_casts(&mut self) {
|
||||
for queue in self.queues.values_mut() {
|
||||
queue.clear_expired_cast();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> GlobalDispatch<ZwlrScreencopyManagerV1, ScreencopyManagerGlobalData, D>
|
||||
@@ -550,10 +614,8 @@ where
|
||||
return;
|
||||
};
|
||||
|
||||
queue.pending_frames.remove(frame);
|
||||
queue
|
||||
.screencopies
|
||||
.retain(|screencopy| screencopy.frame != *frame);
|
||||
queue.remove_frame(frame);
|
||||
|
||||
// Clean up the queue if this was the last object.
|
||||
if queue.is_empty() && !manager.is_alive() {
|
||||
state.queues.remove(manager);
|
||||
|
||||
Reference in New Issue
Block a user