mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-21 03:40:59 +03:00
drm: keep the demoted-display list consistent instead of stretching PipeWire (review 4.5)
A DRM display demoted to PipeWire has no geometry-consistent per-connector stream on a multi-monitor host -- the portal exposes a single whole-desktop stream. The fallthrough served that whole-desktop frame while the list still advertised the demoted connector geometry, so the client stretched the frame and offset all input by the connector origin (the primary-index-0 demotion reaches this even after the get_capturer_for_display exact-index fix). Dropping the display from the list is not an option: its position IS the capturer index, so a drop would shift every later display and desync get_capturer_info. So instead: get_display_infos advertises a multi-monitor demoted display OFFLINE at its stable index, and get_capturer_for_display serves the PipeWire fallback only when its rect matches the advertised geometry, else bails. A single-display host still falls through (whole-desktop == that display). All new logic is drm-gated.
This commit is contained in:
@@ -635,7 +635,27 @@ pub(super) fn get_display_infos() -> Option<Vec<DisplayInfo>> {
|
|||||||
ProbeState::Available(list) => list.clone(),
|
ProbeState::Available(list) => list.clone(),
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
Some(augment_with_wayland_geometry(&list))
|
let multi = list.len() > 1;
|
||||||
|
let mut infos = augment_with_wayland_geometry(&list);
|
||||||
|
// review 4.5: on a multi-monitor host a display demoted to PipeWire has no geometry-consistent
|
||||||
|
// per-connector stream to fall through to -- the portal exposes a single whole-desktop stream, so
|
||||||
|
// serving it for one connector would stretch the frame and offset all input. Advertise such a
|
||||||
|
// display OFFLINE while keeping its list position, so the index space stays aligned with
|
||||||
|
// get_capturer_info() (dropping it would shift every later index) and the client re-enumerates
|
||||||
|
// against a consistent list instead of driving a display the server then refuses. A single-display
|
||||||
|
// host is left online: there the whole-desktop stream IS that display, so the PipeWire fallback is
|
||||||
|
// geometry-consistent and get_capturer_for_display serves it.
|
||||||
|
if multi {
|
||||||
|
let failures = DRM_DISPLAY_FAILURES.lock().unwrap();
|
||||||
|
for (idx, info) in infos.iter_mut().enumerate() {
|
||||||
|
if let Some((count, since)) = failures.get(&(idx as i32)).copied() {
|
||||||
|
if count >= DRM_GRAB_MAX_FAILURES && since.elapsed() < DEMOTE_COOLDOWN {
|
||||||
|
info.online = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(infos)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Index (into the cached DRM display list) of the compositor's PRIMARY output. DRM connector order
|
/// Index (into the cached DRM display list) of the compositor's PRIMARY output. DRM connector order
|
||||||
|
|||||||
@@ -383,6 +383,40 @@ pub(super) fn get_capturer_for_display(
|
|||||||
unsafe {
|
unsafe {
|
||||||
let cap_display_info = &*cap_display_info;
|
let cap_display_info = &*cap_display_info;
|
||||||
let rect = cap_display_info.rects[cap_display_info.current];
|
let rect = cap_display_info.rects[cap_display_info.current];
|
||||||
|
// review 4.5: reaching here with DRM active means get_capturer_info bailed (a demoted
|
||||||
|
// display) and we fell through to PipeWire. Serve this stream ONLY if its rect matches the
|
||||||
|
// geometry we advertised for this index. The portal typically exposes one whole-desktop
|
||||||
|
// stream, so on a multi-monitor host that rect is the FULL desktop while the advertised DRM
|
||||||
|
// geometry is a single connector -> serving it would stretch the frame and offset all
|
||||||
|
// input. Bail instead; get_display_infos advertised the display offline, so the client
|
||||||
|
// re-enumerates against a consistent list. A single-display host matches (whole-desktop ==
|
||||||
|
// that display) and is served normally. On a pure-PipeWire host is_available() is false and
|
||||||
|
// this guard is skipped, preserving upstream behavior exactly.
|
||||||
|
#[cfg(feature = "drm")]
|
||||||
|
if super::drm_capturer::is_available() {
|
||||||
|
if let Some(advertised) = super::drm_capturer::get_display_infos()
|
||||||
|
.and_then(|l| l.get(display_idx).cloned())
|
||||||
|
{
|
||||||
|
let consistent = advertised.x == rect.0 .0
|
||||||
|
&& advertised.y == rect.0 .1
|
||||||
|
&& advertised.width as usize == rect.1
|
||||||
|
&& advertised.height as usize == rect.2;
|
||||||
|
if !consistent {
|
||||||
|
bail!(
|
||||||
|
"drm display {} demoted with no geometry-consistent PipeWire stream (advertised {}x{}+{}+{} vs stream {}x{}+{}+{}); advertised offline",
|
||||||
|
display_idx,
|
||||||
|
advertised.width,
|
||||||
|
advertised.height,
|
||||||
|
advertised.x,
|
||||||
|
advertised.y,
|
||||||
|
rect.1,
|
||||||
|
rect.2,
|
||||||
|
rect.0 .0,
|
||||||
|
rect.0 .1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(super::video_service::CapturerInfo {
|
Ok(super::video_service::CapturerInfo {
|
||||||
origin: rect.0,
|
origin: rect.0,
|
||||||
width: rect.1,
|
width: rect.1,
|
||||||
|
|||||||
Reference in New Issue
Block a user