Merge pull request #4427 from fufesou/feat/custom_resolution
Feat/custom resolution
This commit is contained in:
commit
f35c74133b
@ -995,10 +995,18 @@ class _ResolutionsMenu extends StatefulWidget {
|
|||||||
State<_ResolutionsMenu> createState() => _ResolutionsMenuState();
|
State<_ResolutionsMenu> createState() => _ResolutionsMenuState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const double _kCustonResolutionEditingWidth = 42;
|
||||||
|
const _kCustomResolutionValue = 'custom';
|
||||||
|
|
||||||
class _ResolutionsMenuState extends State<_ResolutionsMenu> {
|
class _ResolutionsMenuState extends State<_ResolutionsMenu> {
|
||||||
String _groupValue = '';
|
String _groupValue = '';
|
||||||
Resolution? _localResolution;
|
Resolution? _localResolution;
|
||||||
|
|
||||||
|
late final TextEditingController _customWidth =
|
||||||
|
TextEditingController(text: display.width.toString());
|
||||||
|
late final TextEditingController _customHeight =
|
||||||
|
TextEditingController(text: display.height.toString());
|
||||||
|
|
||||||
PeerInfo get pi => widget.ffi.ffiModel.pi;
|
PeerInfo get pi => widget.ffi.ffiModel.pi;
|
||||||
FfiModel get ffiModel => widget.ffi.ffiModel;
|
FfiModel get ffiModel => widget.ffi.ffiModel;
|
||||||
Display get display => ffiModel.display;
|
Display get display => ffiModel.display;
|
||||||
@ -1012,22 +1020,20 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isVirtualDisplay = display.isVirtualDisplayResolution;
|
final isVirtualDisplay = display.isVirtualDisplayResolution;
|
||||||
// final visible =
|
final visible =
|
||||||
// ffiModel.keyboard && (isVirtualDisplay || resolutions.length > 1);
|
ffiModel.keyboard && (isVirtualDisplay || resolutions.length > 1);
|
||||||
final visible = ffiModel.keyboard && resolutions.length > 1;
|
|
||||||
if (!visible) return Offstage();
|
if (!visible) return Offstage();
|
||||||
_groupValue = '${display.width}x${display.height}';
|
|
||||||
_getLocalResolution();
|
_getLocalResolution();
|
||||||
final showOriginalBtn =
|
final showOriginalBtn =
|
||||||
display.isOriginalResolutionSet && !display.isOriginalResolution;
|
display.isOriginalResolutionSet && !display.isOriginalResolution;
|
||||||
final showFitLocalBtn = !_isRemoteResolutionFitLocal();
|
final showFitLocalBtn = !_isRemoteResolutionFitLocal();
|
||||||
|
_setGroupValue();
|
||||||
return _SubmenuButton(
|
return _SubmenuButton(
|
||||||
ffi: widget.ffi,
|
ffi: widget.ffi,
|
||||||
menuChildren: <Widget>[
|
menuChildren: <Widget>[
|
||||||
_OriginalResolutionMenuButton(showOriginalBtn),
|
_OriginalResolutionMenuButton(showOriginalBtn),
|
||||||
_FitLocalResolutionMenuButton(showFitLocalBtn),
|
_FitLocalResolutionMenuButton(showFitLocalBtn),
|
||||||
// _customResolutionMenuButton(isVirtualDisplay),
|
_customResolutionMenuButton(isVirtualDisplay),
|
||||||
_menuDivider(showOriginalBtn, showFitLocalBtn, isVirtualDisplay),
|
_menuDivider(showOriginalBtn, showFitLocalBtn, isVirtualDisplay),
|
||||||
] +
|
] +
|
||||||
_supportedResolutionMenuButtons(),
|
_supportedResolutionMenuButtons(),
|
||||||
@ -1035,11 +1041,20 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_setGroupValue() {
|
||||||
|
final lastGroupValue =
|
||||||
|
stateGlobal.getLastResolutionGroupValue(widget.id, pi.currentDisplay);
|
||||||
|
if (lastGroupValue == _kCustomResolutionValue) {
|
||||||
|
_groupValue = _kCustomResolutionValue;
|
||||||
|
} else {
|
||||||
|
_groupValue = '${display.width}x${display.height}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_menuDivider(
|
_menuDivider(
|
||||||
bool showOriginalBtn, bool showFitLocalBtn, bool isVirtualDisplay) {
|
bool showOriginalBtn, bool showFitLocalBtn, bool isVirtualDisplay) {
|
||||||
return Offstage(
|
return Offstage(
|
||||||
// offstage: !(showOriginalBtn || showFitLocalBtn || isVirtualDisplay),
|
offstage: !(showOriginalBtn || showFitLocalBtn || isVirtualDisplay),
|
||||||
offstage: !(showOriginalBtn || showFitLocalBtn),
|
|
||||||
child: Divider(),
|
child: Divider(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1060,12 +1075,25 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_onChanged(String? value) async {
|
_onChanged(String? value) async {
|
||||||
|
stateGlobal.setLastResolutionGroupValue(
|
||||||
|
widget.id, pi.currentDisplay, value);
|
||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
|
|
||||||
|
int? w;
|
||||||
|
int? h;
|
||||||
|
if (value == _kCustomResolutionValue) {
|
||||||
|
w = int.tryParse(_customWidth.text);
|
||||||
|
h = int.tryParse(_customHeight.text);
|
||||||
|
} else {
|
||||||
final list = value.split('x');
|
final list = value.split('x');
|
||||||
if (list.length == 2) {
|
if (list.length == 2) {
|
||||||
final w = int.tryParse(list[0]);
|
w = int.tryParse(list[0]);
|
||||||
final h = int.tryParse(list[1]);
|
h = int.tryParse(list[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (w != null && h != null) {
|
if (w != null && h != null) {
|
||||||
|
if (w != display.width || h != display.height) {
|
||||||
await _changeResolution(w, h);
|
await _changeResolution(w, h);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1117,6 +1145,49 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _customResolutionMenuButton(isVirtualDisplay) {
|
||||||
|
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),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField _resolutionInput(TextEditingController controller) {
|
||||||
|
return TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
isDense: true,
|
||||||
|
contentPadding: EdgeInsets.fromLTRB(3, 3, 3, 3),
|
||||||
|
),
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
inputFormatters: <TextInputFormatter>[
|
||||||
|
FilteringTextInputFormatter.digitsOnly,
|
||||||
|
LengthLimitingTextInputFormatter(4),
|
||||||
|
FilteringTextInputFormatter.allow(RegExp(r'[0-9]')),
|
||||||
|
],
|
||||||
|
controller: controller,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
List<Widget> _supportedResolutionMenuButtons() => resolutions
|
List<Widget> _supportedResolutionMenuButtons() => resolutions
|
||||||
.map((e) => RdoMenuButton(
|
.map((e) => RdoMenuButton(
|
||||||
value: '${e.width}x${e.height}',
|
value: '${e.width}x${e.height}',
|
||||||
@ -1655,14 +1726,14 @@ class RdoMenuButton<T> extends StatelessWidget {
|
|||||||
final ValueChanged<T?>? onChanged;
|
final ValueChanged<T?>? onChanged;
|
||||||
final Widget? child;
|
final Widget? child;
|
||||||
final FFI ffi;
|
final FFI ffi;
|
||||||
const RdoMenuButton(
|
const RdoMenuButton({
|
||||||
{Key? key,
|
Key? key,
|
||||||
required this.value,
|
required this.value,
|
||||||
required this.groupValue,
|
required this.groupValue,
|
||||||
required this.onChanged,
|
|
||||||
required this.child,
|
required this.child,
|
||||||
required this.ffi})
|
required this.ffi,
|
||||||
: super(key: key);
|
this.onChanged,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -503,6 +503,9 @@ class FfiModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stateGlobal.resetLastResolutionGroupValues(peerId);
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,12 +12,14 @@ class StateGlobal {
|
|||||||
bool _maximize = false;
|
bool _maximize = false;
|
||||||
bool grabKeyboard = false;
|
bool grabKeyboard = false;
|
||||||
final RxBool _showTabBar = true.obs;
|
final RxBool _showTabBar = true.obs;
|
||||||
final RxBool _showResizeEdge = true.obs;
|
|
||||||
final RxDouble _resizeEdgeSize = RxDouble(kWindowEdgeSize);
|
final RxDouble _resizeEdgeSize = RxDouble(kWindowEdgeSize);
|
||||||
final RxDouble _windowBorderWidth = RxDouble(kWindowBorderWidth);
|
final RxDouble _windowBorderWidth = RxDouble(kWindowBorderWidth);
|
||||||
final RxBool showRemoteMenuBar = false.obs;
|
final RxBool showRemoteMenuBar = false.obs;
|
||||||
final RxInt displaysCount = 0.obs;
|
final RxInt displaysCount = 0.obs;
|
||||||
|
|
||||||
|
// Use for desktop -> remote toolbar -> resolution
|
||||||
|
final Map<String, Map<int, String?>> _lastResolutionGroupValues = {};
|
||||||
|
|
||||||
int get windowId => _windowId;
|
int get windowId => _windowId;
|
||||||
bool get fullscreen => _fullscreen;
|
bool get fullscreen => _fullscreen;
|
||||||
bool get maximize => _maximize;
|
bool get maximize => _maximize;
|
||||||
@ -26,6 +28,22 @@ class StateGlobal {
|
|||||||
RxDouble get resizeEdgeSize => _resizeEdgeSize;
|
RxDouble get resizeEdgeSize => _resizeEdgeSize;
|
||||||
RxDouble get windowBorderWidth => _windowBorderWidth;
|
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;
|
setWindowId(int id) => _windowId = id;
|
||||||
setMaximize(bool v) {
|
setMaximize(bool v) {
|
||||||
if (_maximize != v && !_fullscreen) {
|
if (_maximize != v && !_fullscreen) {
|
||||||
@ -33,12 +51,12 @@ class StateGlobal {
|
|||||||
_resizeEdgeSize.value = _maximize ? kMaximizeEdgeSize : kWindowEdgeSize;
|
_resizeEdgeSize.value = _maximize ? kMaximizeEdgeSize : kWindowEdgeSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setFullscreen(bool v) {
|
setFullscreen(bool v) {
|
||||||
if (_fullscreen != v) {
|
if (_fullscreen != v) {
|
||||||
_fullscreen = v;
|
_fullscreen = v;
|
||||||
_showTabBar.value = !_fullscreen;
|
_showTabBar.value = !_fullscreen;
|
||||||
_resizeEdgeSize.value =
|
_resizeEdgeSize.value = fullscreen
|
||||||
fullscreen
|
|
||||||
? kFullScreenEdgeSize
|
? kFullScreenEdgeSize
|
||||||
: _maximize
|
: _maximize
|
||||||
? kMaximizeEdgeSize
|
? kMaximizeEdgeSize
|
||||||
|
@ -56,10 +56,6 @@ use windows_service::{
|
|||||||
use winreg::enums::*;
|
use winreg::enums::*;
|
||||||
use winreg::RegKey;
|
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)> {
|
pub fn get_cursor_pos() -> Option<(i32, i32)> {
|
||||||
unsafe {
|
unsafe {
|
||||||
#[allow(invalid_value)]
|
#[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<()> {
|
pub fn change_resolution(name: &str, width: usize, height: usize) -> ResultType<()> {
|
||||||
let device_name = str_to_device_name(name);
|
let device_name = str_to_device_name(name);
|
||||||
|
@ -1940,6 +1940,16 @@ impl Connection {
|
|||||||
fn change_resolution(&mut self, r: &Resolution) {
|
fn change_resolution(&mut self, r: &Resolution) {
|
||||||
if self.keyboard {
|
if self.keyboard {
|
||||||
if let Ok(name) = video_service::get_current_display_name() {
|
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) =
|
if let Err(e) =
|
||||||
crate::platform::change_resolution(&name, r.width as _, r.height as _)
|
crate::platform::change_resolution(&name, r.width as _, r.height as _)
|
||||||
{
|
{
|
||||||
|
@ -897,7 +897,11 @@ pub fn handle_one_frame_encoded(
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_original_resolution(display_name: &str, w: usize, h: usize) -> MessageField<Resolution> {
|
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 {
|
Resolution {
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
@ -909,24 +913,6 @@ fn get_original_resolution(display_name: &str, w: usize, h: usize) -> MessageFie
|
|||||||
.into()
|
.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>) {
|
pub(super) fn get_displays_2(all: &Vec<Display>) -> (usize, Vec<DisplayInfo>) {
|
||||||
let mut displays = Vec::new();
|
let mut displays = Vec::new();
|
||||||
let mut primary = 0;
|
let mut primary = 0;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use hbb_common::{allow_err, bail, lazy_static, log, ResultType};
|
use hbb_common::{allow_err, bail, lazy_static, log, ResultType};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashSet,
|
collections::{HashMap, HashSet},
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -16,8 +16,8 @@ lazy_static::lazy_static! {
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct VirtualDisplayManager {
|
struct VirtualDisplayManager {
|
||||||
headless_index: Option<u32>,
|
headless_index_name: Option<(u32, String)>,
|
||||||
peer_required_indices: HashSet<u32>,
|
peer_index_name: HashMap<u32, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VirtualDisplayManager {
|
impl VirtualDisplayManager {
|
||||||
@ -54,14 +54,16 @@ pub fn plug_in_headless() -> ResultType<()> {
|
|||||||
height: 1080,
|
height: 1080,
|
||||||
sync: 60,
|
sync: 60,
|
||||||
}];
|
}];
|
||||||
|
let device_names = windows::get_device_names();
|
||||||
VirtualDisplayManager::plug_in_monitor(VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS, &modes)?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn plug_out_headless() -> bool {
|
pub fn plug_out_headless() -> bool {
|
||||||
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
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) {
|
if let Err(e) = virtual_display::plug_out_monitor(index) {
|
||||||
log::error!("Plug out monitor failed {}", e);
|
log::error!("Plug out monitor failed {}", e);
|
||||||
}
|
}
|
||||||
@ -71,19 +73,39 @@ pub fn plug_out_headless() -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn plug_in_peer_required(
|
fn get_new_device_name(device_names: &HashSet<String>) -> String {
|
||||||
modes: Vec<Vec<virtual_display::MonitorMode>>,
|
for _ in 0..3 {
|
||||||
) -> ResultType<Vec<u32>> {
|
let device_names_af = windows::get_device_names();
|
||||||
|
let diff_names: Vec<_> = device_names_af.difference(&device_names).collect();
|
||||||
|
if diff_names.len() == 1 {
|
||||||
|
return diff_names[0].clone();
|
||||||
|
} else if diff_names.len() > 1 {
|
||||||
|
log::error!(
|
||||||
|
"Failed to get diff device names after plugin virtual display, more than one diff names: {:?}",
|
||||||
|
&diff_names
|
||||||
|
);
|
||||||
|
return "".to_string();
|
||||||
|
}
|
||||||
|
// Sleep is needed here to wait for the virtual display to be ready.
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(50));
|
||||||
|
}
|
||||||
|
log::error!("Failed to get diff device names after plugin virtual display",);
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn plug_in_peer_request(modes: Vec<Vec<virtual_display::MonitorMode>>) -> ResultType<Vec<u32>> {
|
||||||
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
||||||
VirtualDisplayManager::prepare_driver()?;
|
VirtualDisplayManager::prepare_driver()?;
|
||||||
|
|
||||||
let mut indices: Vec<u32> = Vec::new();
|
let mut indices: Vec<u32> = Vec::new();
|
||||||
for m in modes.iter() {
|
for m in modes.iter() {
|
||||||
for idx in VIRTUAL_DISPLAY_START_FOR_PEER..VIRTUAL_DISPLAY_MAX_COUNT {
|
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) {
|
match VirtualDisplayManager::plug_in_monitor(idx, m) {
|
||||||
Ok(_) => {
|
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);
|
indices.push(idx);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -97,13 +119,135 @@ pub fn plug_in_peer_required(
|
|||||||
Ok(indices)
|
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();
|
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
||||||
for idx in modes.iter() {
|
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));
|
allow_err!(virtual_display::plug_out_monitor(*idx));
|
||||||
manager.peer_required_indices.remove(idx);
|
manager.peer_index_name.remove(idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user