diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 9f05b3f73..1f4f74a33 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -997,7 +997,6 @@ class _ResolutionsMenu extends StatefulWidget { const double _kCustonResolutionEditingWidth = 42; const _kCustomResolutionValue = 'custom'; -String? _lastResolutionGroupValue; class _ResolutionsMenuState extends State<_ResolutionsMenu> { String _groupValue = ''; @@ -1043,7 +1042,9 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { } _setGroupValue() { - if (_lastResolutionGroupValue == _kCustomResolutionValue) { + final lastGroupValue = + stateGlobal.getLastResolutionGroupValue(widget.id, pi.currentDisplay); + if (lastGroupValue == _kCustomResolutionValue) { _groupValue = _kCustomResolutionValue; } else { _groupValue = '${display.width}x${display.height}'; @@ -1053,8 +1054,7 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { _menuDivider( bool showOriginalBtn, bool showFitLocalBtn, bool isVirtualDisplay) { return Offstage( - // offstage: !(showOriginalBtn || showFitLocalBtn || isVirtualDisplay), - offstage: !(showOriginalBtn || showFitLocalBtn), + offstage: !(showOriginalBtn || showFitLocalBtn || isVirtualDisplay), child: Divider(), ); } @@ -1075,7 +1075,8 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { } _onChanged(String? value) async { - _lastResolutionGroupValue = value; + stateGlobal.setLastResolutionGroupValue( + widget.id, pi.currentDisplay, value); if (value == null) return; int? w; @@ -1092,7 +1093,7 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { } if (w != null && h != null) { - if (w != display.width && h != display.height) { + if (w != display.width || h != display.height) { await _changeResolution(w, h); } } @@ -1145,24 +1146,27 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { } Widget _customResolutionMenuButton(isVirtualDisplay) { - return RdoMenuButton( - value: _kCustomResolutionValue, - groupValue: _groupValue, - onChanged: _onChanged, - ffi: widget.ffi, - child: Row( - children: [ - Text('${translate('resolution_custom_tip')} '), - SizedBox( - width: _kCustonResolutionEditingWidth, - child: _resolutionInput(_customWidth), - ), - Text(' x '), - SizedBox( - width: _kCustonResolutionEditingWidth, - child: _resolutionInput(_customHeight), - ), - ], + return Offstage( + offstage: !isVirtualDisplay, + child: RdoMenuButton( + value: _kCustomResolutionValue, + groupValue: _groupValue, + onChanged: _onChanged, + ffi: widget.ffi, + child: Row( + children: [ + Text('${translate('resolution_custom_tip')} '), + SizedBox( + width: _kCustonResolutionEditingWidth, + child: _resolutionInput(_customWidth), + ), + Text(' x '), + SizedBox( + width: _kCustonResolutionEditingWidth, + child: _resolutionInput(_customHeight), + ), + ], + ), ), ); } diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 294b348b9..a99b0ddd7 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -503,6 +503,9 @@ class FfiModel with ChangeNotifier { } } } + + stateGlobal.resetLastResolutionGroupValues(peerId); + notifyListeners(); } diff --git a/flutter/lib/models/state_model.dart b/flutter/lib/models/state_model.dart index 187b1ffc5..72b78dbc2 100644 --- a/flutter/lib/models/state_model.dart +++ b/flutter/lib/models/state_model.dart @@ -12,12 +12,14 @@ class StateGlobal { bool _maximize = false; bool grabKeyboard = false; final RxBool _showTabBar = true.obs; - final RxBool _showResizeEdge = true.obs; final RxDouble _resizeEdgeSize = RxDouble(kWindowEdgeSize); final RxDouble _windowBorderWidth = RxDouble(kWindowBorderWidth); final RxBool showRemoteMenuBar = false.obs; final RxInt displaysCount = 0.obs; + // Use for desktop -> remote toolbar -> resolution + final Map> _lastResolutionGroupValues = {}; + int get windowId => _windowId; bool get fullscreen => _fullscreen; bool get maximize => _maximize; @@ -26,6 +28,22 @@ class StateGlobal { RxDouble get resizeEdgeSize => _resizeEdgeSize; RxDouble get windowBorderWidth => _windowBorderWidth; + resetLastResolutionGroupValues(String peerId) { + _lastResolutionGroupValues[peerId] = {}; + } + + setLastResolutionGroupValue( + String peerId, int currentDisplay, String? value) { + if (!_lastResolutionGroupValues.containsKey(peerId)) { + _lastResolutionGroupValues[peerId] = {}; + } + _lastResolutionGroupValues[peerId]![currentDisplay] = value; + } + + String? getLastResolutionGroupValue(String peerId, int currentDisplay) { + return _lastResolutionGroupValues[peerId]?[currentDisplay]; + } + setWindowId(int id) => _windowId = id; setMaximize(bool v) { if (_maximize != v && !_fullscreen) { @@ -33,12 +51,12 @@ class StateGlobal { _resizeEdgeSize.value = _maximize ? kMaximizeEdgeSize : kWindowEdgeSize; } } + setFullscreen(bool v) { if (_fullscreen != v) { _fullscreen = v; _showTabBar.value = !_fullscreen; - _resizeEdgeSize.value = - fullscreen + _resizeEdgeSize.value = fullscreen ? kFullScreenEdgeSize : _maximize ? kMaximizeEdgeSize diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 46f92d511..e2bb2ca83 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -56,10 +56,6 @@ 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)] @@ -1890,38 +1886,7 @@ pub fn current_resolution(name: &str) -> ResultType { } } -#[inline] -fn is_device_name(device_name: &str, name: &str) -> bool { - if name.len() == device_name.len() { - name == device_name - } else if name.len() > device_name.len() { - false - } else { - &device_name[..name.len()] == name && device_name.as_bytes()[name.len() as usize] == 0 - } -} -pub fn is_virtual_display(name: &str) -> ResultType { - let mut dd: DISPLAY_DEVICEW = unsafe { std::mem::zeroed() }; - dd.cb = std::mem::size_of::() as DWORD; - let mut i_dev_num = 0; - loop { - let result = unsafe { EnumDisplayDevicesW(null_mut(), i_dev_num, &mut dd, 0) }; - if result == 0 { - break; - } - if let Ok(device_name) = String::from_utf16(&dd.DeviceName) { - if is_device_name(&device_name, name) { - return 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), - }; - } - } - i_dev_num += 1; - } - bail!("No such display '{}'", name) -} pub fn change_resolution(name: &str, width: usize, height: usize) -> ResultType<()> { let device_name = str_to_device_name(name); diff --git a/src/server/connection.rs b/src/server/connection.rs index 69cb56102..9bffebf4f 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1940,6 +1940,16 @@ impl Connection { fn change_resolution(&mut self, r: &Resolution) { if self.keyboard { if let Ok(name) = video_service::get_current_display_name() { + #[cfg(target_os = "windows")] + if let Some(_ok) = + crate::virtual_display_manager::change_resolution_if_is_virtual_display( + &name, + r.width as _, + r.height as _, + ) + { + return; + } if let Err(e) = crate::platform::change_resolution(&name, r.width as _, r.height as _) { diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 18b400fca..6bb491758 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -897,7 +897,11 @@ pub fn handle_one_frame_encoded( #[inline] fn get_original_resolution(display_name: &str, w: usize, h: usize) -> MessageField { - Some(if is_virtual_display(&display_name) { + #[cfg(target_os = "windows")] + let is_virtual_display = crate::virtual_display_manager::is_virtual_display(&display_name); + #[cfg(not(target_os = "windows"))] + let is_virtual_display = false; + Some(if is_virtual_display { Resolution { width: 0, height: 0, @@ -909,24 +913,6 @@ fn get_original_resolution(display_name: &str, w: usize, h: usize) -> MessageFie .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; diff --git a/src/virtual_display_manager.rs b/src/virtual_display_manager.rs index 08cc0d485..03cf86e10 100644 --- a/src/virtual_display_manager.rs +++ b/src/virtual_display_manager.rs @@ -1,6 +1,6 @@ use hbb_common::{allow_err, bail, lazy_static, log, ResultType}; use std::{ - collections::HashSet, + collections::{HashMap, HashSet}, sync::{Arc, Mutex}, }; @@ -16,8 +16,8 @@ lazy_static::lazy_static! { #[derive(Default)] struct VirtualDisplayManager { - headless_index: Option, - peer_required_indices: HashSet, + headless_index_name: Option<(u32, String)>, + peer_index_name: HashMap, } impl VirtualDisplayManager { @@ -54,14 +54,16 @@ pub fn plug_in_headless() -> ResultType<()> { height: 1080, sync: 60, }]; + let device_names = windows::get_device_names(); VirtualDisplayManager::plug_in_monitor(VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS, &modes)?; - manager.headless_index = Some(VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS); + let device_name = get_new_device_name(&device_names); + manager.headless_index_name = Some((VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS, device_name)); Ok(()) } pub fn plug_out_headless() -> bool { let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); - if let Some(index) = manager.headless_index.take() { + if let Some((index, _)) = manager.headless_index_name.take() { if let Err(e) = virtual_display::plug_out_monitor(index) { log::error!("Plug out monitor failed {}", e); } @@ -71,19 +73,33 @@ pub fn plug_out_headless() -> bool { } } -pub fn plug_in_peer_required( - modes: Vec>, -) -> ResultType> { +fn get_new_device_name(device_names: &HashSet) -> String { + let device_names_af = windows::get_device_names(); + let diff_names: Vec<_> = device_names_af.difference(&device_names).collect(); + if diff_names.len() != 1 { + log::error!( + "Failed to get diff device names after plugin virtual display : {:?}", + &diff_names + ); + "".to_string() + } else { + diff_names[0].clone() + } +} + +pub fn plug_in_peer_request(modes: Vec>) -> ResultType> { let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); VirtualDisplayManager::prepare_driver()?; let mut indices: Vec = Vec::new(); for m in modes.iter() { for idx in VIRTUAL_DISPLAY_START_FOR_PEER..VIRTUAL_DISPLAY_MAX_COUNT { - if !manager.peer_required_indices.contains(&idx) { + if !manager.peer_index_name.contains_key(&idx) { + let device_names = windows::get_device_names(); match VirtualDisplayManager::plug_in_monitor(idx, m) { Ok(_) => { - manager.peer_required_indices.insert(idx); + let device_name = get_new_device_name(&device_names); + manager.peer_index_name.insert(idx, device_name); indices.push(idx); } Err(e) => { @@ -97,13 +113,135 @@ pub fn plug_in_peer_required( Ok(indices) } -pub fn plug_out_peer_required(modes: &[u32]) -> ResultType<()> { +pub fn plug_out_peer_request(modes: &[u32]) -> ResultType<()> { let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); for idx in modes.iter() { - if manager.peer_required_indices.contains(idx) { + if manager.peer_index_name.contains_key(idx) { allow_err!(virtual_display::plug_out_monitor(*idx)); - manager.peer_required_indices.remove(idx); + manager.peer_index_name.remove(idx); } } Ok(()) } + +pub fn is_virtual_display(name: &str) -> bool { + let lock = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); + if let Some((_, device_name)) = &lock.headless_index_name { + if windows::is_device_name(device_name, name) { + return true; + } + } + for (k, v) in lock.peer_index_name.iter() { + if windows::is_device_name(v, name) { + return true; + } + } + false +} + +fn change_resolution(index: u32, w: u32, h: u32) -> bool { + let modes = [virtual_display::MonitorMode { + width: w, + height: h, + sync: 60, + }]; + match virtual_display::update_monitor_modes(index, &modes) { + Ok(_) => true, + Err(e) => { + log::error!("Update monitor {} modes {:?} failed: {}", index, &modes, e); + false + } + } +} + +pub fn change_resolution_if_is_virtual_display(name: &str, w: u32, h: u32) -> Option { + let lock = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); + if let Some((index, device_name)) = &lock.headless_index_name { + if windows::is_device_name(device_name, name) { + return Some(change_resolution(*index, w, h)); + } + } + + for (k, v) in lock.peer_index_name.iter() { + if windows::is_device_name(v, name) { + return Some(change_resolution(*k, w, h)); + } + } + None +} + +mod windows { + use std::{collections::HashSet, ptr::null_mut}; + use winapi::{ + shared::minwindef::{DWORD, FALSE}, + um::{ + wingdi::{ + DEVMODEW, DISPLAY_DEVICEW, DISPLAY_DEVICE_ACTIVE, DISPLAY_DEVICE_MIRRORING_DRIVER, + }, + winuser::{EnumDisplayDevicesW, EnumDisplaySettingsExW, ENUM_CURRENT_SETTINGS}, + }, + }; + + // 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"; + + #[inline] + pub(super) fn is_device_name(device_name: &str, name: &str) -> bool { + if name.len() == device_name.len() { + name == device_name + } else if name.len() > device_name.len() { + false + } else { + &device_name[..name.len()] == name && device_name.as_bytes()[name.len() as usize] == 0 + } + } + + pub(super) fn get_device_names() -> HashSet { + let mut device_names = HashSet::new(); + let mut dd: DISPLAY_DEVICEW = unsafe { std::mem::zeroed() }; + dd.cb = std::mem::size_of::() as DWORD; + let mut i_dev_num = 0; + loop { + let result = unsafe { EnumDisplayDevicesW(null_mut(), i_dev_num, &mut dd, 0) }; + if result == 0 { + break; + } + i_dev_num += 1; + + if 0 == (dd.StateFlags & DISPLAY_DEVICE_ACTIVE) + || (dd.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) > 0 + { + continue; + } + + let mut dm: DEVMODEW = unsafe { std::mem::zeroed() }; + dm.dmSize = std::mem::size_of::() as _; + dm.dmDriverExtra = 0; + let ok = unsafe { + EnumDisplaySettingsExW( + dd.DeviceName.as_ptr(), + ENUM_CURRENT_SETTINGS, + &mut dm as _, + 0, + ) + }; + if ok == FALSE { + continue; + } + if dm.dmPelsHeight == 0 || dm.dmPelsWidth == 0 { + continue; + } + + if let (Ok(device_name), Ok(device_string)) = ( + String::from_utf16(&dd.DeviceName), + String::from_utf16(&dd.DeviceString), + ) { + if &device_string[..IDD_DEVICE_STRING.len()] == IDD_DEVICE_STRING { + device_names.insert(device_name); + } + } + } + device_names + } +}