mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-15 00:41:01 +03:00
Fix Wayland cursor density in physical desktop layouts
This commit is contained in:
@@ -320,6 +320,9 @@ pub struct WaylandDisplayInfo {
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
pub logical_size: Option<(i32, i32)>,
|
||||
// wl_output.scale is independent of desktop coordinates. Older probes omit it.
|
||||
#[serde(default)]
|
||||
pub scale_factor: i32,
|
||||
pub refresh_rate: i32,
|
||||
/// Output rotation in degrees (0/90/180/270), from `wl_output.geometry`. The mode keeps its
|
||||
/// unrotated dimensions and `logical_size` arrives already swapped, so without this field a
|
||||
@@ -450,6 +453,7 @@ fn collect_wayland_displays(conn: &Connection) -> ResultType<Vec<WaylandDisplayI
|
||||
width,
|
||||
height,
|
||||
logical_size,
|
||||
scale_factor: info.scale_factor,
|
||||
refresh_rate,
|
||||
transform,
|
||||
});
|
||||
@@ -542,9 +546,11 @@ mod tests {
|
||||
let old = r#"{"name":"HDMI-1","x":0,"y":0,"width":1920,"height":1080,"logical_size":null,"refresh_rate":60}"#;
|
||||
let info: WaylandDisplayInfo = serde_json::from_str(old).unwrap();
|
||||
assert_eq!(info.transform, 0);
|
||||
assert_eq!(info.scale_factor, 0);
|
||||
let roundtrip: WaylandDisplayInfo =
|
||||
serde_json::from_str(&serde_json::to_string(&info).unwrap()).unwrap();
|
||||
assert_eq!(roundtrip.transform, 0);
|
||||
assert_eq!(roundtrip.scale_factor, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -356,6 +356,17 @@ pub fn get_layout_for_uinput_live() -> Option<((i32, i32, i32, i32), Vec<Display
|
||||
}
|
||||
match enumerate_displays() {
|
||||
Ok(displays) => {
|
||||
#[cfg(feature = "drm")]
|
||||
{
|
||||
// Output density can change without moving any desktop-coordinate rectangle.
|
||||
let mut cache = DISPLAYS.lock().unwrap();
|
||||
if let Some(updated) = cache
|
||||
.as_deref()
|
||||
.and_then(|cached| updated_cursor_scales(cached, &displays))
|
||||
{
|
||||
*cache = Some(Arc::new(updated));
|
||||
}
|
||||
}
|
||||
desktop_rect_of(&displays).map(|rect| (rect, logical_rects_of(&displays)))
|
||||
}
|
||||
Err(_err) => {
|
||||
@@ -366,6 +377,30 @@ pub fn get_layout_for_uinput_live() -> Option<((i32, i32, i32, i32), Vec<Display
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "drm")]
|
||||
fn updated_cursor_scales(cached: &Displays, live: &[WaylandDisplayInfo]) -> Option<Displays> {
|
||||
let mut displays = cached.displays.clone();
|
||||
let mut changed = false;
|
||||
for output in &mut displays {
|
||||
// Geometry changes retain the existing layout invalidation/rebuild path.
|
||||
let Some(current) = live.iter().find(|d| {
|
||||
d.name == output.name
|
||||
&& (d.x, d.y) == (output.x, output.y)
|
||||
&& (d.width, d.height) == (output.width, output.height)
|
||||
&& d.transform == output.transform
|
||||
&& d.logical_size == output.logical_size
|
||||
}) else {
|
||||
continue;
|
||||
};
|
||||
changed |= output.scale_factor != current.scale_factor;
|
||||
output.scale_factor = current.scale_factor;
|
||||
}
|
||||
changed.then_some(Displays {
|
||||
primary: cached.primary,
|
||||
displays,
|
||||
})
|
||||
}
|
||||
|
||||
fn desktop_rect_of(displays: &[WaylandDisplayInfo]) -> Option<(i32, i32, i32, i32)> {
|
||||
if displays.is_empty() {
|
||||
return None;
|
||||
@@ -604,6 +639,7 @@ mod tests {
|
||||
width,
|
||||
height,
|
||||
logical_size,
|
||||
scale_factor: 1,
|
||||
refresh_rate: 60,
|
||||
transform: 0,
|
||||
}
|
||||
@@ -614,6 +650,33 @@ mod tests {
|
||||
assert_eq!(desktop_rect_of(&[]), None);
|
||||
}
|
||||
|
||||
#[cfg(feature = "drm")]
|
||||
#[test]
|
||||
fn output_scale_refresh_preserves_layout_and_existing_snapshots() {
|
||||
let mut output = display(0, 0, 2560, 1600, Some((2560, 1600)));
|
||||
output.name = "eDP-1".into();
|
||||
output.scale_factor = 3;
|
||||
let cached = Displays {
|
||||
primary: 0,
|
||||
displays: vec![output.clone()],
|
||||
};
|
||||
output.scale_factor = 2;
|
||||
let updated = updated_cursor_scales(&cached, &[output.clone()]).unwrap();
|
||||
assert_eq!(updated.displays[0].scale_factor, 2);
|
||||
assert_eq!(cached.displays[0].scale_factor, 3);
|
||||
assert_eq!(updated.primary, cached.primary);
|
||||
assert_eq!(
|
||||
logical_rects_of(&updated.displays),
|
||||
logical_rects_of(&cached.displays)
|
||||
);
|
||||
assert!(updated_cursor_scales(&updated, &[output.clone()]).is_none());
|
||||
output.name = "HDMI-1".into();
|
||||
assert!(updated_cursor_scales(&cached, &[output.clone()]).is_none());
|
||||
output.name = "eDP-1".into();
|
||||
output.logical_size = Some((1280, 800));
|
||||
assert!(updated_cursor_scales(&cached, &[output]).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_desktop_rect_single_display_uses_physical_size() {
|
||||
let displays = [display(0, 0, 2880, 1800, Some((1859, 1162)))];
|
||||
|
||||
@@ -128,7 +128,14 @@ fn wayland_scale(display: &base::platform::linux::WaylandDisplayInfo) -> ResultT
|
||||
} else {
|
||||
display.width
|
||||
};
|
||||
Ok(f64::from(width) / f64::from(logical_width))
|
||||
let scale = f64::from(width) / f64::from(logical_width);
|
||||
// Mutter can report physical desktop coordinates even at 2x/3x output scale.
|
||||
// Keep geometry-derived fractional densities for logically scaled desktops.
|
||||
Ok(if scale == 1.0 && display.scale_factor > 1 {
|
||||
f64::from(display.scale_factor)
|
||||
} else {
|
||||
scale
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -155,6 +162,7 @@ mod tests {
|
||||
width: 1280,
|
||||
height: 800,
|
||||
logical_size: Some((600, 960)),
|
||||
scale_factor: 2,
|
||||
refresh_rate: 60000,
|
||||
transform: 90,
|
||||
};
|
||||
@@ -162,4 +170,27 @@ mod tests {
|
||||
assert_ne!(cache_id(1, 1.0), cache_id(1, 2.0));
|
||||
assert_eq!(cache_id(1, 0.0), 1);
|
||||
}
|
||||
|
||||
#[cfg(feature = "drm")]
|
||||
#[test]
|
||||
fn cursor_density_distinguishes_output_scale_from_desktop_coordinates() {
|
||||
for (scale_factor, logical_size, expected) in [
|
||||
(3, (2560, 1600), 3.0), // GNOME can use physical desktop coordinates.
|
||||
(2, (2048, 1280), 1.25), // Keep fractional scaling from logical geometry.
|
||||
(0, (2560, 1600), 1.0), // Older probe snapshots omit wl_output.scale.
|
||||
] {
|
||||
let display = base::platform::linux::WaylandDisplayInfo {
|
||||
name: "eDP-1".into(),
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 2560,
|
||||
height: 1600,
|
||||
logical_size: Some(logical_size),
|
||||
scale_factor,
|
||||
refresh_rate: 120000,
|
||||
transform: 0,
|
||||
};
|
||||
assert_eq!(wayland_scale(&display).unwrap(), expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2158,6 +2158,7 @@ mod drm_capturer_tests {
|
||||
width: w,
|
||||
height: h,
|
||||
logical_size: Some((w, h)),
|
||||
scale_factor: 1,
|
||||
refresh_rate: 60,
|
||||
transform: 0,
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ mod tests {
|
||||
width: 1280,
|
||||
height: 800,
|
||||
logical_size: Some((logical_width, logical_width * 800 / 1280)),
|
||||
scale_factor: 1,
|
||||
refresh_rate: 60000,
|
||||
transform: 0,
|
||||
}],
|
||||
|
||||
Reference in New Issue
Block a user