mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-21 02:01:55 +07:00
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:
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user