feat, win10, virtual display, not support message

Signed-off-by: fufesou <shuanglongchen@yeah.ne>
This commit is contained in:
fufesou 2023-11-20 21:44:25 +08:00
parent 3a6f56ebbc
commit 83dbf88ffb
47 changed files with 134 additions and 83 deletions

View File

@ -5,7 +5,7 @@ use std::{
time::Instant,
};
use winapi::{
shared::minwindef::{DWORD, FALSE},
shared::minwindef::{DWORD, FALSE, TRUE},
um::{
handleapi::CloseHandle,
pdh::{
@ -14,7 +14,12 @@ use winapi::{
PDH_HCOUNTER, PDH_HQUERY,
},
synchapi::{CreateEventA, WaitForSingleObject},
winbase::{INFINITE, WAIT_OBJECT_0},
sysinfoapi::VerSetConditionMask,
winbase::{VerifyVersionInfoW, INFINITE, WAIT_OBJECT_0},
winnt::{
OSVERSIONINFOEXW, VER_BUILDNUMBER, VER_GREATER_EQUAL, VER_MAJORVERSION,
VER_MINORVERSION, VER_SERVICEPACKMAJOR, VER_SERVICEPACKMINOR,
},
},
};
@ -152,3 +157,43 @@ pub fn sync_cpu_usage(cpu_usage: Option<f64>) {
*CPU_USAGE_ONE_MINUTE.lock().unwrap() = v;
log::info!("cpu usage synced: {:?}", cpu_usage);
}
// https://learn.microsoft.com/en-us/windows/win32/sysinfo/targeting-your-application-at-windows-8-1
// https://github.com/nodejs/node-convergence-archive/blob/e11fe0c2777561827cdb7207d46b0917ef3c42a7/deps/uv/src/win/util.c#L780
pub fn is_windows_version_or_greater(
os_major: u32,
os_minor: u32,
build_number: u32,
service_pack_major: u32,
service_pack_minor: u32,
) -> bool {
let mut osvi: OSVERSIONINFOEXW = unsafe { std::mem::zeroed() };
osvi.dwOSVersionInfoSize = std::mem::size_of::<OSVERSIONINFOEXW>() as DWORD;
osvi.dwMajorVersion = os_major as _;
osvi.dwMinorVersion = os_minor as _;
osvi.dwBuildNumber = build_number as _;
osvi.wServicePackMajor = service_pack_major as _;
osvi.wServicePackMinor = service_pack_minor as _;
let result = unsafe {
let mut condition_mask = 0;
let op = VER_GREATER_EQUAL;
condition_mask = VerSetConditionMask(condition_mask, VER_MAJORVERSION, op);
condition_mask = VerSetConditionMask(condition_mask, VER_MINORVERSION, op);
condition_mask = VerSetConditionMask(condition_mask, VER_BUILDNUMBER, op);
condition_mask = VerSetConditionMask(condition_mask, VER_SERVICEPACKMAJOR, op);
condition_mask = VerSetConditionMask(condition_mask, VER_SERVICEPACKMINOR, op);
VerifyVersionInfoW(
&mut osvi as *mut OSVERSIONINFOEXW,
VER_MAJORVERSION
| VER_MINORVERSION
| VER_BUILDNUMBER
| VER_SERVICEPACKMAJOR
| VER_SERVICEPACKMINOR,
condition_mask,
)
};
result == TRUE
}

View File

@ -204,7 +204,9 @@ pub fn core_main() -> Option<Vec<String>> {
#[cfg(windows)]
hbb_common::allow_err!(crate::platform::windows::install_cert(&args[1]));
#[cfg(all(windows, feature = "virtual_display_driver"))]
hbb_common::allow_err!(crate::virtual_display_manager::install_update_driver());
if crate::virtual_display_manager::is_virtual_display_supported() {
hbb_common::allow_err!(crate::virtual_display_manager::install_update_driver());
}
return None;
} else if args[0] == "--uninstall-cert" {
#[cfg(windows)]

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", "模式 2"),
("Enter privacy mode", "进入隐私模式"),
("Exit privacy mode", "退出隐私模式"),
("idd_not_support_tip", "不支持 Indirect Display Driver 。需要更新的 Windows 10 版本。"),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -205,5 +205,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("id_input_tip", "You can input an ID, a direct IP, or a domain with a port (<domain>:<port>).\nIf you want to access a device on another server, please append the server address (<id>@<server_address>?key=<key_value>), for example,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nIf you want to access a device on a public server, please input \"<id>@public\", the key is not needed for public server"),
("privacy_mode_impl_mag_tip", "Mode 1"),
("privacy_mode_impl_virtual_display_tip", "Mode 2"),
("idd_not_support_tip", "Indirect Display Driver is not supported. Newer Windows 10 version is required."),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", "Modo 2"),
("Enter privacy mode", "Entrar al modo privado"),
("Exit privacy mode", "Salir del modo privado"),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", "Mode 2"),
("Enter privacy mode", "Masuk mode privasi"),
("Exit privacy mode", "Keluar mode privasi"),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", "Modo 2"),
("Enter privacy mode", "Entra in modalità privacy"),
("Exit privacy mode", "Esci dalla modalità privacy"),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", "모드 2"),
("Enter privacy mode", "개인정보 보호 모드 사용"),
("Exit privacy mode", "개인정보 보호 모드 종료"),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", "2. režīms"),
("Enter privacy mode", "Ieiet privātuma režīmā"),
("Exit privacy mode", "Iziet no privātuma režīma"),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -154,15 +154,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("config_screen", "For å kunne få adgang til ditt skrivebord med fjernstyring, må du gi Rustdesk \"skjerstøtte \" tillatelser."),
("Installing ...", "Installerer ..."),
("Install", "installer"),
("installasjon", "Installasjon"),
("installasjon Path", "Installasjonssti"),
("Installation", ""),
("Installation Path", ""),
("Create start menu shortcuts", "Oppret start meny snarvei"),
("Create desktop icon", "Oppret skrivebords-snarvei"),
("agreement_tip", "Hvis du starter installasjonen, må du akseptere lisensavtalen"),
("Accept and Install", "Aksepter og installer"),
("End-user license agreement", "Lisensavtale for sluttbrukere"),
("Generating ...", "Genererer kode ..."),
("Your installasjon is lower version.", "Din installasjon er en eldre versjon."),
("Your installation is lower version.", ""),
("not_close_tcp_tip", "Ikke lukk dette vinduet, mens du bruker tunnelen."),
("Listening ...", "Lytter ..."),
("Remote Host", "Fjern-Host"),
@ -532,8 +532,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Change Color", ""),
("Primary Color", ""),
("HSV Color", ""),
("installasjon Successful!", ""),
("installasjon failed!", ""),
("Installation Successful!", ""),
("Installation failed!", ""),
("Reverse mouse wheel", ""),
("{} sessions", ""),
("scam_title", ""),
@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", "Tryb 2"),
("Enter privacy mode", "Wejdź w tryb prywatności"),
("Exit privacy mode", "Wyjdź z trybu prywatności"),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", "Режим 2"),
("Enter privacy mode", "Включить режим конфиденциальности"),
("Exit privacy mode", "Отключить режим конфиденциальности"),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", "Режим 2"),
("Enter privacy mode", "Увійти в режим конфіденційності"),
("Exit privacy mode", "Вийти з режиму конфіденційності"),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -572,5 +572,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("privacy_mode_impl_virtual_display_tip", ""),
("Enter privacy mode", ""),
("Exit privacy mode", ""),
("idd_not_support_tip", ""),
].iter().cloned().collect();
}

View File

@ -634,42 +634,3 @@ extern "C"
freopen("CONOUT$", "w", stdout);
}
} // end of extern "C"
extern "C"
{
// https://learn.microsoft.com/en-us/windows/win32/sysinfo/targeting-your-application-at-windows-8-1
// https://github.com/nodejs/node-convergence-archive/blob/e11fe0c2777561827cdb7207d46b0917ef3c42a7/deps/uv/src/win/util.c#L780
BOOL IsWindowsVersionOrGreater(DWORD os_major,
DWORD os_minor,
DWORD build_number,
WORD service_pack_major,
WORD service_pack_minor)
{
OSVERSIONINFOEX osvi;
DWORDLONG condition_mask = 0;
int op = VER_GREATER_EQUAL;
/* Initialize the OSVERSIONINFOEX structure. */
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
osvi.dwMajorVersion = os_major;
osvi.dwMinorVersion = os_minor;
osvi.dwBuildNumber = build_number;
osvi.wServicePackMajor = service_pack_major;
osvi.wServicePackMinor = service_pack_minor;
/* Initialize the condition mask. */
VER_SET_CONDITION(condition_mask, VER_MAJORVERSION, op);
VER_SET_CONDITION(condition_mask, VER_MINORVERSION, op);
VER_SET_CONDITION(condition_mask, VER_BUILDNUMBER, op);
VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMAJOR, op);
VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMINOR, op);
/* Perform the test. */
return VerifyVersionInfo(
&osvi,
VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER |
VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
condition_mask);
}
}

View File

@ -460,13 +460,6 @@ extern "C" {
fn is_win_down() -> BOOL;
fn is_local_system() -> BOOL;
fn alloc_console_and_redirect();
fn IsWindowsVersionOrGreater(
os_major: DWORD,
os_minor: DWORD,
build_number: DWORD,
service_pack_major: WORD,
service_pack_minor: WORD,
) -> BOOL;
}
extern "system" {
@ -1255,25 +1248,6 @@ pub fn block_input(v: bool) -> (bool, String) {
}
}
#[inline]
pub fn is_windows_version_or_greater(
os_major: u32,
os_minor: u32,
build_number: u32,
service_pack_major: u32,
service_pack_minor: u32,
) -> bool {
unsafe {
IsWindowsVersionOrGreater(
os_major as _,
os_minor as _,
build_number as _,
service_pack_major as _,
service_pack_minor as _,
) == TRUE
}
}
pub fn add_recent_document(path: &str) {
extern "C" {
fn AddRecentDocument(path: *const u16);

View File

@ -1,3 +1,5 @@
use hbb_common::platform::windows::is_windows_version_or_greater;
pub use super::win_topmost_window::PrivacyModeImpl;
pub(super) const PRIVACY_MODE_IMPL: &str = "privacy_mode_impl_exclude_from_capture";
@ -5,5 +7,5 @@ pub(super) const PRIVACY_MODE_IMPL: &str = "privacy_mode_impl_exclude_from_captu
pub(super) fn is_supported() -> bool {
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowdisplayaffinity
// https://en.wikipedia.org/wiki/Windows_10_version_history
crate::platform::windows::is_windows_version_or_greater(10, 0, 19041, 0, 0)
is_windows_version_or_greater(10, 0, 19041, 0, 0)
}

View File

@ -357,6 +357,10 @@ impl PrivacyMode for PrivacyModeImpl {
}
fn turn_on_privacy(&mut self, conn_id: i32) -> ResultType<bool> {
if !virtual_display_manager::is_virtual_display_supported() {
bail!("idd_not_support_tip");
}
if self.check_on_conn_id(conn_id)? {
log::debug!("Privacy mode of conn {} is already on", conn_id);
return Ok(true);

View File

@ -2371,14 +2371,19 @@ impl Connection {
};
if t.on {
if let Err(e) = virtual_display_manager::plug_in_index_modes(t.display as _, Vec::new())
{
log::error!("Failed to plug in virtual display: {}", e);
self.send(make_msg(format!(
"Failed to plug in virtual display: {}",
e
)))
.await;
if !virtual_display_manager::is_virtual_display_supported() {
self.send(make_msg("idd_not_support_tip".to_string())).await;
} else {
if let Err(e) =
virtual_display_manager::plug_in_index_modes(t.display as _, Vec::new())
{
log::error!("Failed to plug in virtual display: {}", e);
self.send(make_msg(format!(
"Failed to plug in virtual display: {}",
e
)))
.await;
}
}
} else {
let indices = if t.display == -1 {

View File

@ -348,7 +348,10 @@ pub fn try_get_displays() -> ResultType<Vec<Display>> {
#[cfg(all(windows, feature = "virtual_display_driver"))]
pub fn try_get_displays() -> ResultType<Vec<Display>> {
let mut displays = Display::all()?;
if crate::platform::is_installed() && no_displays(&displays) {
if crate::platform::is_installed()
&& no_displays(&displays)
&& virtual_display_manager::is_virtual_display_supported()
{
log::debug!("no displays, create virtual display");
if let Err(e) = virtual_display_manager::plug_in_headless() {
log::error!("plug in headless failed {}", e);

View File

@ -1,3 +1,5 @@
#[cfg(target_os = "windows")]
use hbb_common::platform::windows::is_windows_version_or_greater;
use hbb_common::{allow_err, bail, lazy_static, log, ResultType};
use std::{
collections::{HashMap, HashSet},
@ -53,6 +55,17 @@ impl VirtualDisplayManager {
}
}
pub fn is_virtual_display_supported() -> bool {
#[cfg(target_os = "windows")]
{
is_windows_version_or_greater(10, 0, 19041, 0, 0)
}
#[cfg(not(target_os = "windows"))]
{
false
}
}
pub fn install_update_driver() -> ResultType<()> {
VIRTUAL_DISPLAY_MANAGER
.lock()
@ -146,6 +159,10 @@ pub fn plug_in_index_modes(
}
pub fn reset_all() -> ResultType<()> {
if is_virtual_display_supported() {
return Ok(());
}
if let Err(e) = plug_out_peer_request(&get_virtual_displays()) {
log::error!("Failed to plug out virtual displays: {}", e);
}