Store cast Stream ID, use it for Redraw request

Unlike StopCast, Redraw targets a specific Cast. Use the stream ID to
identify it.
This commit is contained in:
Ivan Molodetskikh
2025-03-15 10:23:00 +03:00
parent f6aa8c1793
commit 8c4ebb00a1
3 changed files with 99 additions and 117 deletions
+29 -13
View File
@@ -62,6 +62,8 @@ static STREAM_ID: AtomicUsize = AtomicUsize::new(0);
#[derive(Clone)]
pub struct Stream {
id: usize,
session_id: usize,
target: StreamTarget,
cursor_mode: CursorMode,
was_started: Arc<AtomicBool>,
@@ -93,6 +95,7 @@ struct StreamParameters {
pub enum ScreenCastToNiri {
StartCast {
session_id: usize,
stream_id: usize,
target: StreamTargetId,
cursor_mode: CursorMode,
signal_ctx: SignalEmitter<'static>,
@@ -149,7 +152,7 @@ impl Session {
debug!("start");
for (stream, iface) in &*self.streams.lock().unwrap() {
stream.start(self.id, iface.signal_emitter().clone());
stream.start(iface.signal_emitter().clone());
}
}
@@ -204,16 +207,20 @@ impl Session {
return Err(fdo::Error::Failed("monitor is disabled".to_owned()));
}
let path = format!(
"/org/gnome/Mutter/ScreenCast/Stream/u{}",
STREAM_ID.fetch_add(1, Ordering::SeqCst)
);
let stream_id = STREAM_ID.fetch_add(1, Ordering::SeqCst);
let path = format!("/org/gnome/Mutter/ScreenCast/Stream/u{stream_id}");
let path = OwnedObjectPath::try_from(path).unwrap();
let cursor_mode = properties.cursor_mode.unwrap_or_default();
let target = StreamTarget::Output(output);
let stream = Stream::new(target, cursor_mode, self.to_niri.clone());
let stream = Stream::new(
stream_id,
self.id,
target,
cursor_mode,
self.to_niri.clone(),
);
match server.at(&path, stream.clone()).await {
Ok(true) => {
let iface = server.interface(&path).await.unwrap();
@@ -237,10 +244,8 @@ impl Session {
) -> fdo::Result<OwnedObjectPath> {
debug!(?properties, "record_window");
let path = format!(
"/org/gnome/Mutter/ScreenCast/Stream/u{}",
STREAM_ID.fetch_add(1, Ordering::SeqCst)
);
let stream_id = STREAM_ID.fetch_add(1, Ordering::SeqCst);
let path = format!("/org/gnome/Mutter/ScreenCast/Stream/u{stream_id}");
let path = OwnedObjectPath::try_from(path).unwrap();
let cursor_mode = properties.cursor_mode.unwrap_or_default();
@@ -248,7 +253,13 @@ impl Session {
let target = StreamTarget::Window {
id: properties.window_id,
};
let stream = Stream::new(target, cursor_mode, self.to_niri.clone());
let stream = Stream::new(
stream_id,
self.id,
target,
cursor_mode,
self.to_niri.clone(),
);
match server.at(&path, stream.clone()).await {
Ok(true) => {
let iface = server.interface(&path).await.unwrap();
@@ -350,11 +361,15 @@ impl Drop for Session {
impl Stream {
fn new(
id: usize,
session_id: usize,
target: StreamTarget,
cursor_mode: CursorMode,
to_niri: calloop::channel::Sender<ScreenCastToNiri>,
) -> Self {
Self {
id,
session_id,
target,
cursor_mode,
was_started: Arc::new(AtomicBool::new(false)),
@@ -362,13 +377,14 @@ impl Stream {
}
}
fn start(&self, session_id: usize, ctxt: SignalEmitter<'static>) {
fn start(&self, ctxt: SignalEmitter<'static>) {
if self.was_started.load(Ordering::SeqCst) {
return;
}
let msg = ScreenCastToNiri::StartCast {
session_id,
session_id: self.session_id,
stream_id: self.id,
target: self.target.make_id(),
cursor_mode: self.cursor_mode,
signal_ctx: ctxt,
+65 -101
View File
@@ -1592,20 +1592,7 @@ impl State {
pub fn on_pw_msg(&mut self, msg: PwToNiri) {
match msg {
PwToNiri::StopCast { session_id } => self.niri.stop_cast(session_id),
PwToNiri::Redraw(target) => match target {
CastTarget::Output(weak) => {
if let Some(output) = weak.upgrade() {
self.niri.queue_redraw(&output);
}
}
CastTarget::Window { id } => {
self.backend.with_primary_renderer(|renderer| {
// FIXME: target presentation time at the time of window commit?
self.niri
.render_window_for_screen_cast(renderer, id, get_monotonic_time());
});
}
},
PwToNiri::Redraw { stream_id } => self.redraw_cast(stream_id),
PwToNiri::FatalError => {
warn!("stopping PipeWire due to fatal error");
if let Some(pw) = self.niri.pipewire.take() {
@@ -1619,6 +1606,67 @@ impl State {
}
}
#[cfg(feature = "xdp-gnome-screencast")]
fn redraw_cast(&mut self, stream_id: usize) {
let _span = tracy_client::span!("State::redraw_cast");
let casts = &mut self.niri.casts;
let Some(cast) = casts.iter_mut().find(|cast| cast.stream_id == stream_id) else {
warn!("cast to redraw is missing");
return;
};
match &cast.target {
CastTarget::Output(weak) => {
if let Some(output) = weak.upgrade() {
self.niri.queue_redraw(&output);
}
}
CastTarget::Window { id } => {
let mut windows = self.niri.layout.windows();
let Some((_, mapped)) = windows.find(|(_, mapped)| mapped.id().get() == *id) else {
return;
};
// Use the cached output since it will be present even if the output was
// currently disconnected.
let Some(output) = self.niri.mapped_cast_output.get(&mapped.window) else {
return;
};
let scale = Scale::from(output.current_scale().fractional_scale());
let bbox = mapped
.window
.bbox_with_popups()
.to_physical_precise_up(scale);
match cast.ensure_size(bbox.size) {
Ok(CastSizeChange::Ready) => (),
Ok(CastSizeChange::Pending) => return,
Err(err) => {
warn!("error updating stream size, stopping screencast: {err:?}");
drop(windows);
let session_id = cast.session_id;
self.niri.stop_cast(session_id);
return;
}
}
self.backend.with_primary_renderer(|renderer| {
// FIXME: pointer.
let elements = mapped
.render_for_screen_cast(renderer, scale)
.rev()
.collect::<Vec<_>>();
if cast.dequeue_buffer_and_render(renderer, &elements, bbox.size, scale) {
cast.last_frame_time = get_monotonic_time();
}
});
}
}
}
#[cfg(feature = "xdp-gnome-screencast")]
pub fn on_screen_cast_msg(&mut self, msg: ScreenCastToNiri) {
use smithay::reexports::gbm::Modifier;
@@ -1628,13 +1676,14 @@ impl State {
match msg {
ScreenCastToNiri::StartCast {
session_id,
stream_id,
target,
cursor_mode,
signal_ctx,
} => {
let _span = tracy_client::span!("StartCast");
debug!(session_id, "StartCast");
debug!(session_id, stream_id, "StartCast");
let Some(gbm) = self.backend.gbm_device() else {
warn!("error starting screencast: no GBM device available");
@@ -1726,6 +1775,7 @@ impl State {
gbm,
render_formats,
session_id,
stream_id,
target,
size,
refresh,
@@ -4336,92 +4386,6 @@ impl Niri {
}
}
#[cfg(feature = "xdp-gnome-screencast")]
fn render_window_for_screen_cast(
&mut self,
renderer: &mut GlesRenderer,
window_id: u64,
target_presentation_time: Duration,
) {
let _span = tracy_client::span!("Niri::render_window_for_screen_cast");
let mut window = None;
self.layout.with_windows(|mapped, _, _| {
if mapped.id().get() != window_id {
return;
}
window = Some(mapped.window.clone());
});
let Some(window) = window else {
return;
};
// Use the cached output since it will be present even if the output was
// currently disconnected.
let Some(output) = self.mapped_cast_output.get(&window) else {
return;
};
let mut windows = self.layout.windows_for_output(output);
let mapped = windows
.find(|mapped| mapped.id().get() == window_id)
.unwrap();
let scale = Scale::from(output.current_scale().fractional_scale());
let bbox = mapped
.window
.bbox_with_popups()
.to_physical_precise_up(scale);
let mut elements = None;
let mut casts_to_stop = vec![];
let mut casts = mem::take(&mut self.casts);
for cast in &mut casts {
if !cast.is_active.get() {
continue;
}
if cast.target != (CastTarget::Window { id: window_id }) {
continue;
}
match cast.ensure_size(bbox.size) {
Ok(CastSizeChange::Ready) => (),
Ok(CastSizeChange::Pending) => continue,
Err(err) => {
warn!("error updating stream size, stopping screencast: {err:?}");
casts_to_stop.push(cast.session_id);
}
}
if cast.check_time_and_schedule(&self.event_loop, output, target_presentation_time) {
continue;
}
let elements = elements.get_or_insert_with(|| {
// FIXME: pointer.
mapped
.render_for_screen_cast(renderer, scale)
.rev()
.collect::<Vec<_>>()
});
if cast.dequeue_buffer_and_render(renderer, elements, bbox.size, scale) {
cast.last_frame_time = target_presentation_time;
}
}
self.casts = casts;
drop(windows);
for id in casts_to_stop {
self.stop_cast(id);
}
}
pub fn render_for_screencopy_with_damage(
&mut self,
renderer: &mut GlesRenderer,
+5 -3
View File
@@ -60,12 +60,13 @@ pub struct PipeWire {
pub enum PwToNiri {
StopCast { session_id: usize },
Redraw(CastTarget),
Redraw { stream_id: usize },
FatalError,
}
pub struct Cast {
pub session_id: usize,
pub stream_id: usize,
pub stream: Stream,
_listener: StreamListener<()>,
pub is_active: Rc<Cell<bool>>,
@@ -189,6 +190,7 @@ impl PipeWire {
gbm: GbmDevice<DrmDeviceFd>,
formats: FormatSet,
session_id: usize,
stream_id: usize,
target: CastTarget,
size: Size<i32, Physical>,
refresh: u32,
@@ -204,10 +206,9 @@ impl PipeWire {
warn!("error sending StopCast to niri: {err:?}");
}
};
let target_ = target.clone();
let to_niri_ = self.to_niri.clone();
let redraw = move || {
if let Err(err) = to_niri_.send(PwToNiri::Redraw(target_.clone())) {
if let Err(err) = to_niri_.send(PwToNiri::Redraw { stream_id }) {
warn!("error sending Redraw to niri: {err:?}");
}
};
@@ -651,6 +652,7 @@ impl PipeWire {
let cast = Cast {
session_id,
stream_id,
stream,
_listener: listener,
is_active,