mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-21 11:50:59 +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 width: i32,
|
||||||
pub height: i32,
|
pub height: i32,
|
||||||
pub logical_size: Option<(i32, 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,
|
pub refresh_rate: i32,
|
||||||
/// Output rotation in degrees (0/90/180/270), from `wl_output.geometry`. The mode keeps its
|
/// 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
|
/// 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,
|
width,
|
||||||
height,
|
height,
|
||||||
logical_size,
|
logical_size,
|
||||||
|
scale_factor: info.scale_factor,
|
||||||
refresh_rate,
|
refresh_rate,
|
||||||
transform,
|
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 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();
|
let info: WaylandDisplayInfo = serde_json::from_str(old).unwrap();
|
||||||
assert_eq!(info.transform, 0);
|
assert_eq!(info.transform, 0);
|
||||||
|
assert_eq!(info.scale_factor, 0);
|
||||||
let roundtrip: WaylandDisplayInfo =
|
let roundtrip: WaylandDisplayInfo =
|
||||||
serde_json::from_str(&serde_json::to_string(&info).unwrap()).unwrap();
|
serde_json::from_str(&serde_json::to_string(&info).unwrap()).unwrap();
|
||||||
assert_eq!(roundtrip.transform, 0);
|
assert_eq!(roundtrip.transform, 0);
|
||||||
|
assert_eq!(roundtrip.scale_factor, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -356,6 +356,17 @@ pub fn get_layout_for_uinput_live() -> Option<((i32, i32, i32, i32), Vec<Display
|
|||||||
}
|
}
|
||||||
match enumerate_displays() {
|
match enumerate_displays() {
|
||||||
Ok(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)))
|
desktop_rect_of(&displays).map(|rect| (rect, logical_rects_of(&displays)))
|
||||||
}
|
}
|
||||||
Err(_err) => {
|
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)> {
|
fn desktop_rect_of(displays: &[WaylandDisplayInfo]) -> Option<(i32, i32, i32, i32)> {
|
||||||
if displays.is_empty() {
|
if displays.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
@@ -604,6 +639,7 @@ mod tests {
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
logical_size,
|
logical_size,
|
||||||
|
scale_factor: 1,
|
||||||
refresh_rate: 60,
|
refresh_rate: 60,
|
||||||
transform: 0,
|
transform: 0,
|
||||||
}
|
}
|
||||||
@@ -614,6 +650,33 @@ mod tests {
|
|||||||
assert_eq!(desktop_rect_of(&[]), None);
|
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]
|
#[test]
|
||||||
fn test_desktop_rect_single_display_uses_physical_size() {
|
fn test_desktop_rect_single_display_uses_physical_size() {
|
||||||
let displays = [display(0, 0, 2880, 1800, Some((1859, 1162)))];
|
let displays = [display(0, 0, 2880, 1800, Some((1859, 1162)))];
|
||||||
|
|||||||
@@ -128,7 +128,14 @@ fn wayland_scale(display: &base::platform::linux::WaylandDisplayInfo) -> ResultT
|
|||||||
} else {
|
} else {
|
||||||
display.width
|
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)]
|
#[cfg(test)]
|
||||||
@@ -155,6 +162,7 @@ mod tests {
|
|||||||
width: 1280,
|
width: 1280,
|
||||||
height: 800,
|
height: 800,
|
||||||
logical_size: Some((600, 960)),
|
logical_size: Some((600, 960)),
|
||||||
|
scale_factor: 2,
|
||||||
refresh_rate: 60000,
|
refresh_rate: 60000,
|
||||||
transform: 90,
|
transform: 90,
|
||||||
};
|
};
|
||||||
@@ -162,4 +170,27 @@ mod tests {
|
|||||||
assert_ne!(cache_id(1, 1.0), cache_id(1, 2.0));
|
assert_ne!(cache_id(1, 1.0), cache_id(1, 2.0));
|
||||||
assert_eq!(cache_id(1, 0.0), 1);
|
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,
|
width: w,
|
||||||
height: h,
|
height: h,
|
||||||
logical_size: Some((w, h)),
|
logical_size: Some((w, h)),
|
||||||
|
scale_factor: 1,
|
||||||
refresh_rate: 60,
|
refresh_rate: 60,
|
||||||
transform: 0,
|
transform: 0,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ mod tests {
|
|||||||
width: 1280,
|
width: 1280,
|
||||||
height: 800,
|
height: 800,
|
||||||
logical_size: Some((logical_width, logical_width * 800 / 1280)),
|
logical_size: Some((logical_width, logical_width * 800 / 1280)),
|
||||||
|
scale_factor: 1,
|
||||||
refresh_rate: 60000,
|
refresh_rate: 60000,
|
||||||
transform: 0,
|
transform: 0,
|
||||||
}],
|
}],
|
||||||
|
|||||||
Reference in New Issue
Block a user