windows, custom resolution

Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
fufesou 2023-05-19 20:48:47 +08:00
parent 5f10d1aae6
commit df2de0fd61
7 changed files with 218 additions and 94 deletions

View File

@ -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),
),
],
),
),
);
}

View File

@ -503,6 +503,9 @@ class FfiModel with ChangeNotifier {
}
}
}
stateGlobal.resetLastResolutionGroupValues(peerId);
notifyListeners();
}

View File

@ -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<String, Map<int, String?>> _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

View File

@ -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<Resolution> {
}
}
#[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<bool> {
let mut dd: DISPLAY_DEVICEW = unsafe { std::mem::zeroed() };
dd.cb = std::mem::size_of::<DISPLAY_DEVICEW>() 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);

View File

@ -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 _)
{

View File

@ -897,7 +897,11 @@ pub fn handle_one_frame_encoded(
#[inline]
fn get_original_resolution(display_name: &str, w: usize, h: usize) -> MessageField<Resolution> {
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<Display>) -> (usize, Vec<DisplayInfo>) {
let mut displays = Vec::new();
let mut primary = 0;

View File

@ -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<u32>,
peer_required_indices: HashSet<u32>,
headless_index_name: Option<(u32, String)>,
peer_index_name: HashMap<u32, String>,
}
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<Vec<virtual_display::MonitorMode>>,
) -> ResultType<Vec<u32>> {
fn get_new_device_name(device_names: &HashSet<String>) -> 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<Vec<virtual_display::MonitorMode>>) -> ResultType<Vec<u32>> {
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
VirtualDisplayManager::prepare_driver()?;
let mut indices: Vec<u32> = 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<bool> {
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<String> {
let mut device_names = HashSet::new();
let mut dd: DISPLAY_DEVICEW = unsafe { std::mem::zeroed() };
dd.cb = std::mem::size_of::<DISPLAY_DEVICEW>() 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::<DEVMODEW>() 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
}
}