Skip to content

Commit 06ab987

Browse files
authored
fix: macos, hidpi, resolutions (rustdesk#11825)
Signed-off-by: fufesou <linlong1266@gmail.com>
1 parent b4a30ca commit 06ab987

5 files changed

Lines changed: 90 additions & 24 deletions

File tree

flutter/lib/models/model.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3212,7 +3212,8 @@ class Display {
32123212
originalWidth == kVirtualDisplayResolutionValue &&
32133213
originalHeight == kVirtualDisplayResolutionValue;
32143214
bool get isOriginalResolution =>
3215-
width == originalWidth && height == originalHeight;
3215+
width == (originalWidth * scale).round() &&
3216+
height == (originalHeight * scale).round();
32163217
}
32173218

32183219
class Resolution {

src/platform/macos.mm

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,28 @@ size_t bitDepth(CGDisplayModeRef mode) {
153153
return depth;
154154
}
155155

156+
static bool isHiDPIMode(CGDisplayModeRef mode) {
157+
// Check if the mode is HiDPI by comparing pixel width to width
158+
// If pixel width is greater than width, it's a HiDPI mode
159+
return CGDisplayModeGetPixelWidth(mode) > CGDisplayModeGetWidth(mode);
160+
}
161+
162+
CFArrayRef getAllModes(CGDirectDisplayID display) {
163+
// Create options dictionary to include HiDPI modes
164+
CFMutableDictionaryRef options = CFDictionaryCreateMutable(
165+
kCFAllocatorDefault,
166+
0,
167+
&kCFTypeDictionaryKeyCallBacks,
168+
&kCFTypeDictionaryValueCallBacks);
169+
// Include HiDPI modes
170+
CFDictionarySetValue(options, kCGDisplayShowDuplicateLowResolutionModes, kCFBooleanTrue);
171+
CFArrayRef allModes = CGDisplayCopyAllDisplayModes(display, options);
172+
CFRelease(options);
173+
return allModes;
174+
}
175+
156176
extern "C" bool MacGetModeNum(CGDirectDisplayID display, uint32_t *numModes) {
157-
CFArrayRef allModes = CGDisplayCopyAllDisplayModes(display, NULL);
177+
CFArrayRef allModes = getAllModes(display);
158178
if (allModes == NULL) {
159179
return false;
160180
}
@@ -163,12 +183,12 @@ size_t bitDepth(CGDisplayModeRef mode) {
163183
return true;
164184
}
165185

166-
extern "C" bool MacGetModes(CGDirectDisplayID display, uint32_t *widths, uint32_t *heights, uint32_t max, uint32_t *numModes) {
186+
extern "C" bool MacGetModes(CGDirectDisplayID display, uint32_t *widths, uint32_t *heights, bool *hidpis, uint32_t max, uint32_t *numModes) {
167187
CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(display);
168188
if (currentMode == NULL) {
169189
return false;
170190
}
171-
CFArrayRef allModes = CGDisplayCopyAllDisplayModes(display, NULL);
191+
CFArrayRef allModes = getAllModes(display);
172192
if (allModes == NULL) {
173193
CGDisplayModeRelease(currentMode);
174194
return false;
@@ -181,6 +201,7 @@ size_t bitDepth(CGDisplayModeRef mode) {
181201
bitDepth(currentMode) == bitDepth(mode)) {
182202
widths[realNum] = (uint32_t)CGDisplayModeGetWidth(mode);
183203
heights[realNum] = (uint32_t)CGDisplayModeGetHeight(mode);
204+
hidpis[realNum] = isHiDPIMode(mode);
184205
realNum++;
185206
}
186207
}
@@ -201,7 +222,6 @@ size_t bitDepth(CGDisplayModeRef mode) {
201222
return true;
202223
}
203224

204-
205225
static bool setDisplayToMode(CGDirectDisplayID display, CGDisplayModeRef mode) {
206226
CGError rc;
207227
CGDisplayConfigRef config;
@@ -220,30 +240,48 @@ static bool setDisplayToMode(CGDirectDisplayID display, CGDisplayModeRef mode) {
220240
return true;
221241
}
222242

223-
extern "C" bool MacSetMode(CGDirectDisplayID display, uint32_t width, uint32_t height)
243+
extern "C" bool MacSetMode(CGDirectDisplayID display, uint32_t width, uint32_t height, bool tryHiDPI)
224244
{
225245
bool ret = false;
226246
CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(display);
227247
if (currentMode == NULL) {
228248
return ret;
229249
}
230-
CFArrayRef allModes = CGDisplayCopyAllDisplayModes(display, NULL);
250+
CFArrayRef allModes = getAllModes(display);
251+
231252
if (allModes == NULL) {
232253
CGDisplayModeRelease(currentMode);
233254
return ret;
234255
}
235256
int numModes = CFArrayGetCount(allModes);
257+
CGDisplayModeRef preferredHiDPIMode = NULL;
258+
CGDisplayModeRef fallbackMode = NULL;
236259
for (int i = 0; i < numModes; i++) {
237260
CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, i);
238261
if (width == CGDisplayModeGetWidth(mode) &&
239262
height == CGDisplayModeGetHeight(mode) &&
240263
CGDisplayModeGetRefreshRate(currentMode) == CGDisplayModeGetRefreshRate(mode) &&
241264
bitDepth(currentMode) == bitDepth(mode)) {
242-
ret = setDisplayToMode(display, mode);
243-
break;
265+
266+
if (isHiDPIMode(mode)) {
267+
preferredHiDPIMode = mode;
268+
break;
269+
} else {
270+
fallbackMode = mode;
271+
if (!tryHiDPI) {
272+
break;
273+
}
274+
}
244275
}
245276
}
277+
278+
if (preferredHiDPIMode) {
279+
ret = setDisplayToMode(display, preferredHiDPIMode);
280+
} else if (fallbackMode) {
281+
ret = setDisplayToMode(display, fallbackMode);
282+
}
283+
246284
CGDisplayModeRelease(currentMode);
247285
CFRelease(allModes);
248286
return ret;
249-
}
287+
}

src/platform/macos.rs

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,13 @@ extern "C" {
5454
display: u32,
5555
widths: *mut u32,
5656
heights: *mut u32,
57+
hidpis: *mut BOOL,
5758
max: u32,
5859
numModes: *mut u32,
5960
) -> BOOL;
6061
fn majorVersion() -> u32;
6162
fn MacGetMode(display: u32, width: *mut u32, height: *mut u32) -> BOOL;
62-
fn MacSetMode(display: u32, width: u32, height: u32) -> BOOL;
63+
fn MacSetMode(display: u32, width: u32, height: u32, tryHiDPI: bool) -> BOOL;
6364
}
6465

6566
pub fn major_version() -> u32 {
@@ -895,27 +896,45 @@ pub fn resolutions(name: &str) -> Vec<Resolution> {
895896
let mut num = 0;
896897
unsafe {
897898
if YES == MacGetModeNum(display, &mut num) {
898-
let (mut widths, mut heights) = (vec![0; num as _], vec![0; num as _]);
899+
let (mut widths, mut heights, mut _hidpis) =
900+
(vec![0; num as _], vec![0; num as _], vec![NO; num as _]);
899901
let mut real_num = 0;
900902
if YES
901903
== MacGetModes(
902904
display,
903905
widths.as_mut_ptr(),
904906
heights.as_mut_ptr(),
907+
_hidpis.as_mut_ptr(),
905908
num,
906909
&mut real_num,
907910
)
908911
{
909912
if real_num <= num {
910-
for i in 0..real_num {
911-
let resolution = Resolution {
913+
v = (0..real_num)
914+
.map(|i| Resolution {
912915
width: widths[i as usize] as _,
913916
height: heights[i as usize] as _,
914917
..Default::default()
915-
};
916-
if !v.contains(&resolution) {
917-
v.push(resolution);
918+
})
919+
.collect::<Vec<_>>();
920+
// Sort by (w, h), desc
921+
v.sort_by(|a, b| {
922+
if a.width == b.width {
923+
b.height.cmp(&a.height)
924+
} else {
925+
b.width.cmp(&a.width)
918926
}
927+
});
928+
// Remove duplicates
929+
v.dedup_by(|a, b| a.width == b.width && a.height == b.height);
930+
// Filter out the ones that are less than width 800 (800x600) if there are too many.
931+
// We can also do this filtering on the client side, but it is better not to change the client side to reduce the impact.
932+
if v.len() > 15 {
933+
// Most width > 800, so it's ok to remove the small ones.
934+
v.retain(|r| r.width >= 800);
935+
}
936+
if v.len() > 15 {
937+
// Ignore if the length is still too long.
919938
}
920939
}
921940
}
@@ -943,7 +962,7 @@ pub fn current_resolution(name: &str) -> ResultType<Resolution> {
943962
pub fn change_resolution_directly(name: &str, width: usize, height: usize) -> ResultType<()> {
944963
let display = name.parse::<u32>().map_err(|e| anyhow!(e))?;
945964
unsafe {
946-
if NO == MacSetMode(display, width as _, height as _) {
965+
if NO == MacSetMode(display, width as _, height as _, true) {
947966
bail!("MacSetMode failed");
948967
}
949968
}

src/server/connection.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3093,10 +3093,18 @@ impl Connection {
30933093
if virtual_display_manager::amyuni_idd::is_my_display(&name) {
30943094
record_changed = false;
30953095
}
3096+
#[cfg(not(target_os = "macos"))]
3097+
let scale = 1.0;
3098+
#[cfg(target_os = "macos")]
3099+
let scale = display.scale();
3100+
let original = (
3101+
((display.width() as f64) / scale).round() as _,
3102+
(display.height() as f64 / scale).round() as _,
3103+
);
30963104
if record_changed {
30973105
display_service::set_last_changed_resolution(
30983106
&name,
3099-
(display.width() as _, display.height() as _),
3107+
original,
31003108
(r.width, r.height),
31013109
);
31023110
}
@@ -4424,7 +4432,7 @@ mod raii {
44244432
*WALLPAPER_REMOVER.lock().unwrap() = None;
44254433
}
44264434
#[cfg(not(any(target_os = "android", target_os = "ios")))]
4427-
display_service::reset_resolutions();
4435+
display_service::restore_resolutions();
44284436
#[cfg(windows)]
44294437
let _ = virtual_display_manager::reset_all();
44304438
#[cfg(target_os = "linux")]

src/server/display_service.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,20 +133,21 @@ pub fn set_last_changed_resolution(display_name: &str, original: (i32, i32), cha
133133

134134
#[inline]
135135
#[cfg(not(any(target_os = "android", target_os = "ios")))]
136-
pub fn reset_resolutions() {
136+
pub fn restore_resolutions() {
137137
for (name, res) in CHANGED_RESOLUTIONS.read().unwrap().iter() {
138138
let (w, h) = res.original;
139+
log::info!("Restore resolution of display '{}' to ({}, {})", name, w, h);
139140
if let Err(e) = crate::platform::change_resolution(name, w as _, h as _) {
140141
log::error!(
141-
"Failed to reset resolution of display '{}' to ({},{}): {}",
142+
"Failed to restore resolution of display '{}' to ({},{}): {}",
142143
name,
143144
w,
144145
h,
145146
e
146147
);
147148
}
148149
}
149-
// Can be cleared because reset resolutions is called when there is no client connected.
150+
// Can be cleared because restore resolutions is called when there is no client connected.
150151
CHANGED_RESOLUTIONS.write().unwrap().clear();
151152
}
152153

@@ -404,7 +405,6 @@ fn no_displays(displays: &Vec<Display>) -> bool {
404405
}
405406
}
406407

407-
408408
#[inline]
409409
#[cfg(not(windows))]
410410
pub fn try_get_displays() -> ResultType<Vec<Display>> {

0 commit comments

Comments
 (0)