diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/dbus/mutter_screen_cast.rs | 42 | ||||
| -rw-r--r-- | src/niri.rs | 166 | ||||
| -rw-r--r-- | src/pw_utils.rs | 8 |
3 files changed, 99 insertions, 117 deletions
diff --git a/src/dbus/mutter_screen_cast.rs b/src/dbus/mutter_screen_cast.rs index a19535ae..d7918989 100644 --- a/src/dbus/mutter_screen_cast.rs +++ b/src/dbus/mutter_screen_cast.rs @@ -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, diff --git a/src/niri.rs b/src/niri.rs index b7829d0e..a045e4dd 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -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() { @@ -1620,6 +1607,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, diff --git a/src/pw_utils.rs b/src/pw_utils.rs index 7cc3a88e..5cc16d85 100644 --- a/src/pw_utils.rs +++ b/src/pw_utils.rs @@ -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, |
