diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index da91e0dce..6d48777ed 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1019,17 +1019,22 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { @override Widget build(BuildContext context) { - final visible = ffiModel.keyboard && resolutions.length > 1; + final isVirtualDisplay = display.isVirtualDisplayResolution; + final visible = + ffiModel.keyboard && (isVirtualDisplay || resolutions.length > 1); if (!visible) return Offstage(); - _groupValue = "${display.width}x${display.height}"; - + _groupValue = '${display.width}x${display.height}'; _getLocalResolution(); + final showOriginalBtn = + display.isOriginalResolutionSet && !display.isOriginalResolution; + final showFitLocalBtn = !_isRemoteResolutionFitLocal(); + return _SubmenuButton( ffi: widget.ffi, menuChildren: [ - _OriginalResolutionMenuButton(), - _FitLocalResolutionMenuButton(), - _customResolutionMenuButton(), + _OriginalResolutionMenuButton(showOriginalBtn), + _FitLocalResolutionMenuButton(showFitLocalBtn), + _customResolutionMenuButton(isVirtualDisplay), ] + _supportedResolutionMenuButtons(), child: Text(translate("Resolution"))); @@ -1065,8 +1070,6 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { h = resolution.height; } } else if (value == _kResolutionCustom) { - debugPrint( - 'REMOVE ME ======================= ${_customWidth.value} ${_customHeight.value}'); w = int.tryParse(_customWidth.value as String); h = int.tryParse(_customHeight.value as String); } else { @@ -1094,9 +1097,9 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { } } - Widget _OriginalResolutionMenuButton() { + Widget _OriginalResolutionMenuButton(bool showOriginalBtn) { return Offstage( - offstage: display.isOriginalResolution, + offstage: !showOriginalBtn, child: RdoMenuButton( value: _kResolutionOrigin, groupValue: _groupValue, @@ -1108,16 +1111,16 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { ); } - Widget _FitLocalResolutionMenuButton() { + Widget _FitLocalResolutionMenuButton(bool showFitLocalBtn) { return Offstage( - offstage: _isRemoteResolutionFitLocal(), + offstage: !showFitLocalBtn, child: RdoMenuButton( value: _kResolutionFitLocal, groupValue: _groupValue, onChanged: _onChanged, ffi: widget.ffi, child: Text( - '${translate('Fit Local')} ${display.originalWidth}x${display.originalHeight}'), + '${translate('Fit Local')} ${_localResolution?.width ?? 0}x${_localResolution?.height ?? 0}'), ), ); } @@ -1131,9 +1134,9 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { child: Text('${e.width}x${e.height}'))) .toList(); - Widget _customResolutionMenuButton() { + Widget _customResolutionMenuButton(bool showCustomBtn) { return Offstage( - offstage: _isRemoteResolutionFitLocal(), + offstage: !showCustomBtn, child: RdoMenuButton( value: _kResolutionCustom, groupValue: _groupValue, @@ -1148,18 +1151,18 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { return Column( children: [ Text(translate('Custom')), - SizedBox( + Container( width: 5, ), - _resolutionInput(_customWidth), - SizedBox( + // _resolutionInput(_customWidth), + Container( width: 3, ), Text('x'), - SizedBox( + Container( width: 3, ), - _resolutionInput(_customHeight), + // _resolutionInput(_customHeight), ], ); } @@ -1185,21 +1188,21 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { return null; } + if (display.isVirtualDisplayResolution) { + return _localResolution!; + } + squareDistance(Resolution lhs, Resolution rhs) => (lhs.width - rhs.width) * (lhs.width - rhs.width) + (lhs.height - rhs.height) * (lhs.height - rhs.height); - Resolution? res; + Resolution res = Resolution(display.width, display.height); for (final r in resolutions) { if (r.width <= _localResolution!.width && r.height <= _localResolution!.height) { - if (res == null) { + if (squareDistance(r, _localResolution!) < + squareDistance(res, _localResolution!)) { res = r; - } else { - if (squareDistance(r, _localResolution!) < - squareDistance(res, _localResolution!)) { - res = r; - } } } } diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index a57a51752..294b348b9 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -301,9 +301,9 @@ class FfiModel with ChangeNotifier { newDisplay.height = int.tryParse(evt['height']) ?? newDisplay.height; newDisplay.cursorEmbedded = int.tryParse(evt['cursor_embedded']) == 1; newDisplay.originalWidth = - int.tryParse(evt['original_width']) ?? newDisplay.originalWidth; + int.tryParse(evt['original_width']) ?? kInvalidResolutionValue; newDisplay.originalHeight = - int.tryParse(evt['original_height']) ?? newDisplay.originalHeight; + int.tryParse(evt['original_height']) ?? kInvalidResolutionValue; _updateCurDisplay(peerId, newDisplay); @@ -537,8 +537,8 @@ class FfiModel with ChangeNotifier { d.width = evt['width'] ?? d.width; d.height = evt['height'] ?? d.height; d.cursorEmbedded = evt['cursor_embedded'] == 1; - d.originalWidth = evt['original_width'] ?? d.originalWidth; - d.originalHeight = evt['original_height'] ?? d.originalHeight; + d.originalWidth = evt['original_width'] ?? kInvalidResolutionValue; + d.originalHeight = evt['original_height'] ?? kInvalidResolutionValue; return d; } @@ -1714,14 +1714,17 @@ class FFI { } } +const kInvalidResolutionValue = -1; +const kVirtualDisplayResolutionValue = 0; + class Display { double x = 0; double y = 0; int width = 0; int height = 0; bool cursorEmbedded = false; - int originalWidth = 0; - int originalHeight = 0; + int originalWidth = kInvalidResolutionValue; + int originalHeight = kInvalidResolutionValue; Display() { width = (isDesktop || isWebDesktop) @@ -1745,6 +1748,12 @@ class Display { other.height == height && other.cursorEmbedded == cursorEmbedded; + bool get isOriginalResolutionSet => + originalWidth != kInvalidResolutionValue && + originalHeight != kInvalidResolutionValue; + bool get isVirtualDisplayResolution => + originalWidth == kVirtualDisplayResolutionValue && + originalHeight == kVirtualDisplayResolutionValue; bool get isOriginalResolution => width == originalWidth && height == originalHeight; } diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 97a4d9c13..431bfd929 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -56,6 +56,10 @@ use windows_service::{ use winreg::enums::*; use winreg::RegKey; +// This string is defined here. +// https://github.com/fufesou/RustDeskIddDriver/blob/b370aad3f50028b039aad211df60c8051c4a64d6/RustDeskIddDriver/RustDeskIddDriver.inf#LL73C1-L73C40 +const IDD_DEVICE_STRING: &'static str = "RustDeskIddDriver Device\0"; + pub fn get_cursor_pos() -> Option<(i32, i32)> { unsafe { #[allow(invalid_value)] @@ -1831,21 +1835,25 @@ pub fn set_path_permission(dir: &PathBuf, permission: &str) -> ResultType<()> { Ok(()) } +#[inline] +fn str_to_device_name(name: &str) -> [u16; 32] { + let mut device_name: Vec = wide_string(name); + if device_name.len() < 32 { + device_name.resize(32, 0); + } + let mut result = [0; 32]; + result.copy_from_slice(&device_name[..32]); + result +} + pub fn resolutions(name: &str) -> Vec { unsafe { let mut dm: DEVMODEW = std::mem::zeroed(); - let wname = wide_string(name); - let len = if wname.len() <= dm.dmDeviceName.len() { - wname.len() - } else { - dm.dmDeviceName.len() - }; - std::ptr::copy_nonoverlapping(wname.as_ptr(), dm.dmDeviceName.as_mut_ptr(), len); - dm.dmSize = std::mem::size_of::() as _; let mut v = vec![]; let mut num = 0; + let device_name = str_to_device_name(name); loop { - if EnumDisplaySettingsW(NULL as _, num, &mut dm) == 0 { + if EnumDisplaySettingsW(device_name.as_ptr(), num, &mut dm) == 0 { break; } let r = Resolution { @@ -1866,8 +1874,8 @@ pub fn current_resolution(name: &str) -> ResultType { unsafe { let mut dm: DEVMODEW = std::mem::zeroed(); dm.dmSize = std::mem::size_of::() as _; - let wname = wide_string(name); - if EnumDisplaySettingsW(wname.as_ptr(), ENUM_CURRENT_SETTINGS, &mut dm) == 0 { + let device_name = str_to_device_name(name); + if EnumDisplaySettingsW(device_name.as_ptr(), ENUM_CURRENT_SETTINGS, &mut dm) == 0 { bail!( "failed to get currrent resolution, errno={}", GetLastError() @@ -1882,29 +1890,46 @@ pub fn current_resolution(name: &str) -> ResultType { } } +pub fn is_virtual_display(name: &str) -> ResultType { + let device_name = str_to_device_name(name); + let mut dd: DISPLAY_DEVICEW = unsafe { std::mem::zeroed() }; + dd.cb = std::mem::size_of::() as _; + let ok = unsafe { EnumDisplayDevicesW(device_name.as_ptr(), 0, &mut dd as _, 0) }; + if ok == FALSE { + bail!( + "enumerate display devices with device name '{}', errno {}", + name, + unsafe { GetLastError() } + ); + } + match std::string::String::from_utf16(&dd.DeviceString) { + Ok(s) => Ok(&s[..IDD_DEVICE_STRING.len()] == IDD_DEVICE_STRING), + Err(e) => bail!( + "convert the device string of '{}' to string: {}", + name, + e + ), + } +} + pub fn change_resolution(name: &str, width: usize, height: usize) -> ResultType<()> { + let device_name = str_to_device_name(name); unsafe { let mut dm: DEVMODEW = std::mem::zeroed(); - if FALSE == EnumDisplaySettingsW(NULL as _, ENUM_CURRENT_SETTINGS, &mut dm) { + if FALSE == EnumDisplaySettingsW(device_name.as_ptr() as _, ENUM_CURRENT_SETTINGS, &mut dm) + { bail!("EnumDisplaySettingsW failed, errno={}", GetLastError()); } - - // to-do: check if need change - println!("REMOVE ME ========================== dm ({},{}) to ({},{})", dm.dmPelsWidth, dm.dmPelsHeight, width, height); - - let wname = wide_string(name); - let len = if wname.len() <= dm.dmDeviceName.len() { - wname.len() - } else { - dm.dmDeviceName.len() - }; - std::ptr::copy_nonoverlapping(wname.as_ptr(), dm.dmDeviceName.as_mut_ptr(), len); - dm.dmSize = std::mem::size_of::() as _; + // dmPelsWidth and dmPelsHeight is the same to width and height + // Because this process is running in dpi awareness mode. + if dm.dmPelsWidth == width as u32 && dm.dmPelsHeight == height as u32 { + return Ok(()); + } dm.dmPelsWidth = width as _; dm.dmPelsHeight = height as _; dm.dmFields = DM_PELSHEIGHT | DM_PELSWIDTH; let res = ChangeDisplaySettingsExW( - wname.as_ptr(), + device_name.as_ptr(), &mut dm, NULL as _, CDS_UPDATEREGISTRY | CDS_GLOBAL | CDS_RESET, diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 00b75c8d2..5a63a60a6 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -25,9 +25,12 @@ use crate::virtual_display_manager; use crate::{platform::windows::is_process_consent_running, privacy_win_mag}; #[cfg(windows)] use hbb_common::get_version_number; -use hbb_common::tokio::sync::{ - mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, - Mutex as TokioMutex, +use hbb_common::{ + protobuf::MessageField, + tokio::sync::{ + mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, + Mutex as TokioMutex, + }, }; #[cfg(not(windows))] use scrap::Capturer; @@ -65,8 +68,9 @@ lazy_static::lazy_static! { static ref ORIGINAL_RESOLUTIONS: Arc>> = Default::default(); } +// Not virtual display #[inline] -pub fn set_original_resolution(display_name: &str, wh: (i32, i32)) -> (i32, i32) { +fn set_original_resolution_(display_name: &str, wh: (i32, i32)) -> (i32, i32) { let mut original_resolutions = ORIGINAL_RESOLUTIONS.write().unwrap(); match original_resolutions.get(display_name) { Some(r) => r.clone(), @@ -77,8 +81,9 @@ pub fn set_original_resolution(display_name: &str, wh: (i32, i32)) -> (i32, i32) } } +// Not virtual display #[inline] -fn get_original_resolution(display_name: &str) -> Option<(i32, i32)> { +fn get_original_resolution_(display_name: &str) -> Option<(i32, i32)> { ORIGINAL_RESOLUTIONS .read() .unwrap() @@ -86,13 +91,25 @@ fn get_original_resolution(display_name: &str) -> Option<(i32, i32)> { .map(|r| r.clone()) } +// Not virtual display #[inline] -fn get_or_set_original_resolution(display_name: &str, wh: (i32, i32)) -> (i32, i32) { - let r = get_original_resolution(display_name); +fn get_or_set_original_resolution_(display_name: &str, wh: (i32, i32)) -> (i32, i32) { + let r = get_original_resolution_(display_name); if let Some(r) = r { return r; } - set_original_resolution(display_name, wh) + set_original_resolution_(display_name, wh) +} + +// Not virtual display +#[inline] +fn update_get_original_resolution_(display_name: &str, w: usize, h: usize) -> Resolution { + let wh = get_or_set_original_resolution_(display_name, (w as _, h as _)); + Resolution { + width: wh.0, + height: wh.1, + ..Default::default() + } } #[inline] @@ -543,10 +560,13 @@ fn run(sp: GenericService) -> ResultType<()> { if *SWITCH.lock().unwrap() { log::debug!("Broadcasting display switch"); let mut misc = Misc::new(); - let display_name = get_current_display_name(); - - // to-do: check if is virtual display - + let display_name = get_current_display_name().unwrap_or_default(); + println!( + "REMOVE ME ============================ display_name: {:?}, is_virtual: {}", + display_name, + is_virtual_display(&display_name) + ); + let original_resolution = get_original_resolution(&display_name, c.width, c.height); misc.set_switch_display(SwitchDisplay { display: c.current as _, x: c.origin.0 as _, @@ -556,19 +576,15 @@ fn run(sp: GenericService) -> ResultType<()> { cursor_embedded: capture_cursor_embedded(), #[cfg(not(any(target_os = "android", target_os = "ios")))] resolutions: Some(SupportedResolutions { - resolutions: display_name - .as_ref() - .map(|name| crate::platform::resolutions(name)) - .unwrap_or(vec![]), + resolutions: if display_name.is_empty() { + vec![] + } else { + crate::platform::resolutions(&display_name) + }, ..SupportedResolutions::default() }) .into(), - original_resolution: Some(update_get_original_resolution( - &display_name.unwrap_or_default(), - c.width, - c.height, - )) - .into(), + original_resolution, ..Default::default() }); let mut msg_out = Message::new(); @@ -884,15 +900,37 @@ pub fn handle_one_frame_encoded( } #[inline] -fn update_get_original_resolution(display_name: &str, w: usize, h: usize) -> Resolution { - let wh = get_or_set_original_resolution(display_name, (w as _, h as _)); - Resolution { - width: wh.0, - height: wh.1, - ..Default::default() +fn get_original_resolution(display_name: &str, w: usize, h: usize) -> MessageField { + Some(if is_virtual_display(&display_name) { + Resolution { + width: 0, + height: 0, + ..Default::default() + } + } else { + update_get_original_resolution_(&display_name, w, h) + }) + .into() +} + +#[inline] +#[cfg(target_os = "windows")] +fn is_virtual_display(name: &str) -> bool { + match crate::platform::windows::is_virtual_display(&name) { + Ok(b) => b, + Err(e) => { + log::error!("Failed to check is virtual display for '{}': {}", &name, e); + false + } } } +#[inline] +#[cfg(not(target_os = "windows"))] +fn is_virtual_display(_name: &str) -> bool { + false +} + pub(super) fn get_displays_2(all: &Vec) -> (usize, Vec) { let mut displays = Vec::new(); let mut primary = 0; @@ -900,20 +938,17 @@ pub(super) fn get_displays_2(all: &Vec) -> (usize, Vec) { if d.is_primary() { primary = i; } + let display_name = d.name(); + let original_resolution = get_original_resolution(&display_name, d.width(), d.height()); displays.push(DisplayInfo { x: d.origin().0 as _, y: d.origin().1 as _, width: d.width() as _, height: d.height() as _, - name: d.name(), + name: display_name, online: d.is_online(), cursor_embedded: false, - original_resolution: Some(update_get_original_resolution( - &d.name(), - d.width(), - d.height(), - )) - .into(), + original_resolution, ..Default::default() }); }