mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-04-18 18:31:28 +03:00
fix: macos, hidpi, resolutions (#11825)
Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
@@ -3212,7 +3212,8 @@ class Display {
|
|||||||
originalWidth == kVirtualDisplayResolutionValue &&
|
originalWidth == kVirtualDisplayResolutionValue &&
|
||||||
originalHeight == kVirtualDisplayResolutionValue;
|
originalHeight == kVirtualDisplayResolutionValue;
|
||||||
bool get isOriginalResolution =>
|
bool get isOriginalResolution =>
|
||||||
width == originalWidth && height == originalHeight;
|
width == (originalWidth * scale).round() &&
|
||||||
|
height == (originalHeight * scale).round();
|
||||||
}
|
}
|
||||||
|
|
||||||
class Resolution {
|
class Resolution {
|
||||||
|
|||||||
@@ -153,8 +153,28 @@ size_t bitDepth(CGDisplayModeRef mode) {
|
|||||||
return depth;
|
return depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool isHiDPIMode(CGDisplayModeRef mode) {
|
||||||
|
// Check if the mode is HiDPI by comparing pixel width to width
|
||||||
|
// If pixel width is greater than width, it's a HiDPI mode
|
||||||
|
return CGDisplayModeGetPixelWidth(mode) > CGDisplayModeGetWidth(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
CFArrayRef getAllModes(CGDirectDisplayID display) {
|
||||||
|
// Create options dictionary to include HiDPI modes
|
||||||
|
CFMutableDictionaryRef options = CFDictionaryCreateMutable(
|
||||||
|
kCFAllocatorDefault,
|
||||||
|
0,
|
||||||
|
&kCFTypeDictionaryKeyCallBacks,
|
||||||
|
&kCFTypeDictionaryValueCallBacks);
|
||||||
|
// Include HiDPI modes
|
||||||
|
CFDictionarySetValue(options, kCGDisplayShowDuplicateLowResolutionModes, kCFBooleanTrue);
|
||||||
|
CFArrayRef allModes = CGDisplayCopyAllDisplayModes(display, options);
|
||||||
|
CFRelease(options);
|
||||||
|
return allModes;
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" bool MacGetModeNum(CGDirectDisplayID display, uint32_t *numModes) {
|
extern "C" bool MacGetModeNum(CGDirectDisplayID display, uint32_t *numModes) {
|
||||||
CFArrayRef allModes = CGDisplayCopyAllDisplayModes(display, NULL);
|
CFArrayRef allModes = getAllModes(display);
|
||||||
if (allModes == NULL) {
|
if (allModes == NULL) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -163,12 +183,12 @@ extern "C" bool MacGetModeNum(CGDirectDisplayID display, uint32_t *numModes) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" bool MacGetModes(CGDirectDisplayID display, uint32_t *widths, uint32_t *heights, uint32_t max, uint32_t *numModes) {
|
extern "C" bool MacGetModes(CGDirectDisplayID display, uint32_t *widths, uint32_t *heights, bool *hidpis, uint32_t max, uint32_t *numModes) {
|
||||||
CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(display);
|
CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(display);
|
||||||
if (currentMode == NULL) {
|
if (currentMode == NULL) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
CFArrayRef allModes = CGDisplayCopyAllDisplayModes(display, NULL);
|
CFArrayRef allModes = getAllModes(display);
|
||||||
if (allModes == NULL) {
|
if (allModes == NULL) {
|
||||||
CGDisplayModeRelease(currentMode);
|
CGDisplayModeRelease(currentMode);
|
||||||
return false;
|
return false;
|
||||||
@@ -181,6 +201,7 @@ extern "C" bool MacGetModes(CGDirectDisplayID display, uint32_t *widths, uint32_
|
|||||||
bitDepth(currentMode) == bitDepth(mode)) {
|
bitDepth(currentMode) == bitDepth(mode)) {
|
||||||
widths[realNum] = (uint32_t)CGDisplayModeGetWidth(mode);
|
widths[realNum] = (uint32_t)CGDisplayModeGetWidth(mode);
|
||||||
heights[realNum] = (uint32_t)CGDisplayModeGetHeight(mode);
|
heights[realNum] = (uint32_t)CGDisplayModeGetHeight(mode);
|
||||||
|
hidpis[realNum] = isHiDPIMode(mode);
|
||||||
realNum++;
|
realNum++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,7 +222,6 @@ extern "C" bool MacGetMode(CGDirectDisplayID display, uint32_t *width, uint32_t
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool setDisplayToMode(CGDirectDisplayID display, CGDisplayModeRef mode) {
|
static bool setDisplayToMode(CGDirectDisplayID display, CGDisplayModeRef mode) {
|
||||||
CGError rc;
|
CGError rc;
|
||||||
CGDisplayConfigRef config;
|
CGDisplayConfigRef config;
|
||||||
@@ -220,30 +240,48 @@ static bool setDisplayToMode(CGDirectDisplayID display, CGDisplayModeRef mode) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" bool MacSetMode(CGDirectDisplayID display, uint32_t width, uint32_t height)
|
extern "C" bool MacSetMode(CGDirectDisplayID display, uint32_t width, uint32_t height, bool tryHiDPI)
|
||||||
{
|
{
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(display);
|
CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(display);
|
||||||
if (currentMode == NULL) {
|
if (currentMode == NULL) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
CFArrayRef allModes = CGDisplayCopyAllDisplayModes(display, NULL);
|
CFArrayRef allModes = getAllModes(display);
|
||||||
|
|
||||||
if (allModes == NULL) {
|
if (allModes == NULL) {
|
||||||
CGDisplayModeRelease(currentMode);
|
CGDisplayModeRelease(currentMode);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
int numModes = CFArrayGetCount(allModes);
|
int numModes = CFArrayGetCount(allModes);
|
||||||
|
CGDisplayModeRef preferredHiDPIMode = NULL;
|
||||||
|
CGDisplayModeRef fallbackMode = NULL;
|
||||||
for (int i = 0; i < numModes; i++) {
|
for (int i = 0; i < numModes; i++) {
|
||||||
CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, i);
|
CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, i);
|
||||||
if (width == CGDisplayModeGetWidth(mode) &&
|
if (width == CGDisplayModeGetWidth(mode) &&
|
||||||
height == CGDisplayModeGetHeight(mode) &&
|
height == CGDisplayModeGetHeight(mode) &&
|
||||||
CGDisplayModeGetRefreshRate(currentMode) == CGDisplayModeGetRefreshRate(mode) &&
|
CGDisplayModeGetRefreshRate(currentMode) == CGDisplayModeGetRefreshRate(mode) &&
|
||||||
bitDepth(currentMode) == bitDepth(mode)) {
|
bitDepth(currentMode) == bitDepth(mode)) {
|
||||||
ret = setDisplayToMode(display, mode);
|
|
||||||
break;
|
if (isHiDPIMode(mode)) {
|
||||||
|
preferredHiDPIMode = mode;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
fallbackMode = mode;
|
||||||
|
if (!tryHiDPI) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (preferredHiDPIMode) {
|
||||||
|
ret = setDisplayToMode(display, preferredHiDPIMode);
|
||||||
|
} else if (fallbackMode) {
|
||||||
|
ret = setDisplayToMode(display, fallbackMode);
|
||||||
|
}
|
||||||
|
|
||||||
CGDisplayModeRelease(currentMode);
|
CGDisplayModeRelease(currentMode);
|
||||||
CFRelease(allModes);
|
CFRelease(allModes);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,12 +54,13 @@ extern "C" {
|
|||||||
display: u32,
|
display: u32,
|
||||||
widths: *mut u32,
|
widths: *mut u32,
|
||||||
heights: *mut u32,
|
heights: *mut u32,
|
||||||
|
hidpis: *mut BOOL,
|
||||||
max: u32,
|
max: u32,
|
||||||
numModes: *mut u32,
|
numModes: *mut u32,
|
||||||
) -> BOOL;
|
) -> BOOL;
|
||||||
fn majorVersion() -> u32;
|
fn majorVersion() -> u32;
|
||||||
fn MacGetMode(display: u32, width: *mut u32, height: *mut u32) -> BOOL;
|
fn MacGetMode(display: u32, width: *mut u32, height: *mut u32) -> BOOL;
|
||||||
fn MacSetMode(display: u32, width: u32, height: u32) -> BOOL;
|
fn MacSetMode(display: u32, width: u32, height: u32, tryHiDPI: bool) -> BOOL;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn major_version() -> u32 {
|
pub fn major_version() -> u32 {
|
||||||
@@ -895,27 +896,45 @@ pub fn resolutions(name: &str) -> Vec<Resolution> {
|
|||||||
let mut num = 0;
|
let mut num = 0;
|
||||||
unsafe {
|
unsafe {
|
||||||
if YES == MacGetModeNum(display, &mut num) {
|
if YES == MacGetModeNum(display, &mut num) {
|
||||||
let (mut widths, mut heights) = (vec![0; num as _], vec![0; num as _]);
|
let (mut widths, mut heights, mut _hidpis) =
|
||||||
|
(vec![0; num as _], vec![0; num as _], vec![NO; num as _]);
|
||||||
let mut real_num = 0;
|
let mut real_num = 0;
|
||||||
if YES
|
if YES
|
||||||
== MacGetModes(
|
== MacGetModes(
|
||||||
display,
|
display,
|
||||||
widths.as_mut_ptr(),
|
widths.as_mut_ptr(),
|
||||||
heights.as_mut_ptr(),
|
heights.as_mut_ptr(),
|
||||||
|
_hidpis.as_mut_ptr(),
|
||||||
num,
|
num,
|
||||||
&mut real_num,
|
&mut real_num,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if real_num <= num {
|
if real_num <= num {
|
||||||
for i in 0..real_num {
|
v = (0..real_num)
|
||||||
let resolution = Resolution {
|
.map(|i| Resolution {
|
||||||
width: widths[i as usize] as _,
|
width: widths[i as usize] as _,
|
||||||
height: heights[i as usize] as _,
|
height: heights[i as usize] as _,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
})
|
||||||
if !v.contains(&resolution) {
|
.collect::<Vec<_>>();
|
||||||
v.push(resolution);
|
// Sort by (w, h), desc
|
||||||
|
v.sort_by(|a, b| {
|
||||||
|
if a.width == b.width {
|
||||||
|
b.height.cmp(&a.height)
|
||||||
|
} else {
|
||||||
|
b.width.cmp(&a.width)
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
// Remove duplicates
|
||||||
|
v.dedup_by(|a, b| a.width == b.width && a.height == b.height);
|
||||||
|
// Filter out the ones that are less than width 800 (800x600) if there are too many.
|
||||||
|
// We can also do this filtering on the client side, but it is better not to change the client side to reduce the impact.
|
||||||
|
if v.len() > 15 {
|
||||||
|
// Most width > 800, so it's ok to remove the small ones.
|
||||||
|
v.retain(|r| r.width >= 800);
|
||||||
|
}
|
||||||
|
if v.len() > 15 {
|
||||||
|
// Ignore if the length is still too long.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -943,7 +962,7 @@ pub fn current_resolution(name: &str) -> ResultType<Resolution> {
|
|||||||
pub fn change_resolution_directly(name: &str, width: usize, height: usize) -> ResultType<()> {
|
pub fn change_resolution_directly(name: &str, width: usize, height: usize) -> ResultType<()> {
|
||||||
let display = name.parse::<u32>().map_err(|e| anyhow!(e))?;
|
let display = name.parse::<u32>().map_err(|e| anyhow!(e))?;
|
||||||
unsafe {
|
unsafe {
|
||||||
if NO == MacSetMode(display, width as _, height as _) {
|
if NO == MacSetMode(display, width as _, height as _, true) {
|
||||||
bail!("MacSetMode failed");
|
bail!("MacSetMode failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3093,10 +3093,18 @@ impl Connection {
|
|||||||
if virtual_display_manager::amyuni_idd::is_my_display(&name) {
|
if virtual_display_manager::amyuni_idd::is_my_display(&name) {
|
||||||
record_changed = false;
|
record_changed = false;
|
||||||
}
|
}
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
let scale = 1.0;
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
let scale = display.scale();
|
||||||
|
let original = (
|
||||||
|
((display.width() as f64) / scale).round() as _,
|
||||||
|
(display.height() as f64 / scale).round() as _,
|
||||||
|
);
|
||||||
if record_changed {
|
if record_changed {
|
||||||
display_service::set_last_changed_resolution(
|
display_service::set_last_changed_resolution(
|
||||||
&name,
|
&name,
|
||||||
(display.width() as _, display.height() as _),
|
original,
|
||||||
(r.width, r.height),
|
(r.width, r.height),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -4424,7 +4432,7 @@ mod raii {
|
|||||||
*WALLPAPER_REMOVER.lock().unwrap() = None;
|
*WALLPAPER_REMOVER.lock().unwrap() = None;
|
||||||
}
|
}
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
display_service::reset_resolutions();
|
display_service::restore_resolutions();
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let _ = virtual_display_manager::reset_all();
|
let _ = virtual_display_manager::reset_all();
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
|
|||||||
@@ -133,12 +133,13 @@ pub fn set_last_changed_resolution(display_name: &str, original: (i32, i32), cha
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
pub fn reset_resolutions() {
|
pub fn restore_resolutions() {
|
||||||
for (name, res) in CHANGED_RESOLUTIONS.read().unwrap().iter() {
|
for (name, res) in CHANGED_RESOLUTIONS.read().unwrap().iter() {
|
||||||
let (w, h) = res.original;
|
let (w, h) = res.original;
|
||||||
|
log::info!("Restore resolution of display '{}' to ({}, {})", name, w, h);
|
||||||
if let Err(e) = crate::platform::change_resolution(name, w as _, h as _) {
|
if let Err(e) = crate::platform::change_resolution(name, w as _, h as _) {
|
||||||
log::error!(
|
log::error!(
|
||||||
"Failed to reset resolution of display '{}' to ({},{}): {}",
|
"Failed to restore resolution of display '{}' to ({},{}): {}",
|
||||||
name,
|
name,
|
||||||
w,
|
w,
|
||||||
h,
|
h,
|
||||||
@@ -146,7 +147,7 @@ pub fn reset_resolutions() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Can be cleared because reset resolutions is called when there is no client connected.
|
// Can be cleared because restore resolutions is called when there is no client connected.
|
||||||
CHANGED_RESOLUTIONS.write().unwrap().clear();
|
CHANGED_RESOLUTIONS.write().unwrap().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,7 +405,6 @@ fn no_displays(displays: &Vec<Display>) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
pub fn try_get_displays() -> ResultType<Vec<Display>> {
|
pub fn try_get_displays() -> ResultType<Vec<Display>> {
|
||||||
|
|||||||
Reference in New Issue
Block a user