diff --git a/Cargo.lock b/Cargo.lock index 505a6f2d6..098189693 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2776,6 +2776,7 @@ dependencies = [ "confy", "directories-next", "dirs-next", + "dlopen", "env_logger 0.10.0", "filetime", "flexi_logger", @@ -5118,7 +5119,6 @@ dependencies = [ "dbus-crossroads", "default-net", "dispatch", - "dlopen", "enigo", "errno", "evdev", @@ -6451,7 +6451,6 @@ version = "0.1.0" dependencies = [ "hbb_common", "lazy_static", - "libloading", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 3579501c7..94ef44e0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ default = ["use_dasp"] hwcodec = ["scrap/hwcodec"] mediacodec = ["scrap/mediacodec"] linux_headless = ["pam", "users"] +virtual_display_driver = ["virtual_display"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -64,7 +65,6 @@ flutter_rust_bridge = { version = "1.61.1", optional = true } errno = "0.3" rdev = { git = "https://github.com/fufesou/rdev" } url = { version = "2.1", features = ["serde"] } -dlopen = "0.1" crossbeam-queue = "0.3" hex = "0.4" reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls"], default-features=false } @@ -94,7 +94,7 @@ winit = "0.26" winapi = { version = "0.3", features = ["winuser", "wincrypt"] } winreg = "0.10" windows-service = "0.4" -virtual_display = { path = "libs/virtual_display" } +virtual_display = { path = "libs/virtual_display", optional = true } impersonate_system = { git = "https://github.com/21pages/impersonate-system" } shared_memory = "0.12" shutdown_hooks = "0.1" diff --git a/build.py b/build.py index fb9e213db..053d59c47 100755 --- a/build.py +++ b/build.py @@ -41,8 +41,8 @@ def get_version(): def parse_rc_features(feature): available_features = { 'IddDriver': { - 'zip_url': 'https://github.com/fufesou/RustDeskIddDriver/releases/download/v0.1/RustDeskIddDriver_x64.zip', - 'checksum_url': 'https://github.com/fufesou/RustDeskIddDriver/releases/download/v0.1/checksum_md5', + 'zip_url': 'https://github.com/fufesou/RustDeskIddDriver/releases/download/v0.3/RustDeskIddDriver_x64.zip', + 'checksum_url': 'https://github.com/fufesou/RustDeskIddDriver/releases/download/v0.3/checksum_md5', 'exclude': ['README.md', 'certmgr.exe', 'install_cert_runas_admin.bat'], }, 'PrivacyMode': { @@ -242,6 +242,7 @@ def get_features(args): features = ['inline'] if not args.flutter else [] if windows: features.extend(get_rc_features(args)) + features.append('virtual_display_driver') if args.hwcodec: features.append('hwcodec') if args.flutter: diff --git a/libs/hbb_common/Cargo.toml b/libs/hbb_common/Cargo.toml index ed9ec73be..4ce9ef37e 100644 --- a/libs/hbb_common/Cargo.toml +++ b/libs/hbb_common/Cargo.toml @@ -34,6 +34,7 @@ tokio-socks = { git = "https://github.com/open-trade/tokio-socks" } chrono = "0.4" backtrace = "0.3" libc = "0.2" +dlopen = "0.1" [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] mac_address = "1.1" diff --git a/libs/hbb_common/src/lib.rs b/libs/hbb_common/src/lib.rs index 799093d24..7d841613d 100644 --- a/libs/hbb_common/src/lib.rs +++ b/libs/hbb_common/src/lib.rs @@ -44,6 +44,8 @@ pub use libc; pub mod keyboard; #[cfg(not(any(target_os = "android", target_os = "ios")))] pub use sysinfo; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +pub use dlopen; #[cfg(feature = "quic")] pub type Stream = quic::Connection; diff --git a/libs/virtual_display/Cargo.toml b/libs/virtual_display/Cargo.toml index c700bd12a..2559ffbc0 100644 --- a/libs/virtual_display/Cargo.toml +++ b/libs/virtual_display/Cargo.toml @@ -7,5 +7,4 @@ edition = "2021" [dependencies] lazy_static = "1.4" -libloading = "0.7" -hbb_common = { path = "../hbb_common" } +hbb_common = { path = "../hbb_common" } \ No newline at end of file diff --git a/libs/virtual_display/dylib/src/lib.rs b/libs/virtual_display/dylib/src/lib.rs index 4a95e3461..3b83d297c 100644 --- a/libs/virtual_display/dylib/src/lib.rs +++ b/libs/virtual_display/dylib/src/lib.rs @@ -2,18 +2,21 @@ pub mod win10; use hbb_common::{bail, lazy_static, ResultType}; -use std::{path::Path, sync::Mutex}; +use std::path::Path; +#[cfg(windows)] +use std::sync::Mutex; + +#[cfg(windows)] lazy_static::lazy_static! { // If device is uninstalled though "Device Manager" Window. // Rustdesk is unable to handle device any more... static ref H_SW_DEVICE: Mutex = Mutex::new(0); - static ref MONITOR_PLUGIN: Mutex> = Mutex::new(Vec::new()); } #[no_mangle] #[cfg(windows)] -pub fn get_dirver_install_path() -> &'static str { +pub fn get_driver_install_path() -> &'static str { win10::DRIVER_INSTALL_PATH } @@ -137,68 +140,48 @@ pub fn close_device() { unsafe { win10::idd::DeviceClose(*H_SW_DEVICE.lock().unwrap() as win10::idd::HSWDEVICE); *H_SW_DEVICE.lock().unwrap() = 0; - MONITOR_PLUGIN.lock().unwrap().clear(); } } #[no_mangle] -pub fn plug_in_monitor() -> ResultType<()> { +pub fn plug_in_monitor(_monitor_index: u32, _edid: u32, _retries: u32) -> ResultType<()> { #[cfg(windows)] unsafe { - let monitor_index = 0 as u32; - let mut plug_in_monitors = MONITOR_PLUGIN.lock().unwrap(); - for i in 0..plug_in_monitors.len() { - if let Some(d) = plug_in_monitors.get(i) { - if *d == monitor_index { - return Ok(()); - } - }; - } - if win10::idd::MonitorPlugIn(monitor_index, 0, 30) == win10::idd::FALSE { - bail!("{}", win10::get_last_msg()?); - } - (*plug_in_monitors).push(monitor_index); - } - Ok(()) -} - -#[no_mangle] -pub fn plug_out_monitor() -> ResultType<()> { - #[cfg(windows)] - unsafe { - let monitor_index = 0 as u32; - if win10::idd::MonitorPlugOut(monitor_index) == win10::idd::FALSE { - bail!("{}", win10::get_last_msg()?); - } - let mut plug_in_monitors = MONITOR_PLUGIN.lock().unwrap(); - for i in 0..plug_in_monitors.len() { - if let Some(d) = plug_in_monitors.get(i) { - if *d == monitor_index { - plug_in_monitors.remove(i); - break; - } - }; - } - } - Ok(()) -} - -#[no_mangle] -pub fn update_monitor_modes() -> ResultType<()> { - #[cfg(windows)] - unsafe { - let monitor_index = 0 as u32; - let mut modes = vec![win10::idd::MonitorMode { - width: 1920, - height: 1080, - sync: 60, - }]; - if win10::idd::FALSE - == win10::idd::MonitorModesUpdate( - monitor_index as win10::idd::UINT, - modes.len() as win10::idd::UINT, - modes.as_mut_ptr(), - ) + if win10::idd::MonitorPlugIn(_monitor_index as _, _edid as _, _retries as _) + == win10::idd::FALSE + { + bail!("{}", win10::get_last_msg()?); + } + } + Ok(()) +} + +#[no_mangle] +pub fn plug_out_monitor(_monitor_index: u32) -> ResultType<()> { + #[cfg(windows)] + unsafe { + if win10::idd::MonitorPlugOut(_monitor_index) == win10::idd::FALSE { + bail!("{}", win10::get_last_msg()?); + } + } + Ok(()) +} + +#[cfg(windows)] +type PMonitorMode = win10::idd::PMonitorMode; +#[cfg(not(windows))] +type PMonitorMode = *mut std::ffi::c_void; + +#[no_mangle] +pub fn update_monitor_modes( + _monitor_index: u32, + _mode_count: u32, + _modes: PMonitorMode, +) -> ResultType<()> { + #[cfg(windows)] + unsafe { + if win10::idd::FALSE + == win10::idd::MonitorModesUpdate(_monitor_index as _, _mode_count as _, _modes) { bail!("{}", win10::get_last_msg()?); } diff --git a/libs/virtual_display/dylib/src/win10/IddController.c b/libs/virtual_display/dylib/src/win10/IddController.c index c1faccfc2..6c240657a 100644 --- a/libs/virtual_display/dylib/src/win10/IddController.c +++ b/libs/virtual_display/dylib/src/win10/IddController.c @@ -196,7 +196,7 @@ BOOL DeviceCreate(PHSWDEVICE hSwDevice) } if (created == TRUE) { - SetLastMsg("Device is created before, please uninstall it first\n"); + SetLastMsg("Device is already created, please destroy it first\n"); if (g_printMsg) { printf(g_lastMsg); @@ -288,7 +288,7 @@ BOOL MonitorPlugIn(UINT index, UINT edid, INT retries) if (retries < 0) { - SetLastMsg("invalid tries %d\n", retries); + SetLastMsg("Invalid tries %d\n", retries); if (g_printMsg) { printf(g_lastMsg); diff --git a/libs/virtual_display/examples/virtual_display_1.rs b/libs/virtual_display/examples/virtual_display_1.rs index 31fdbe06e..e5e1ae554 100644 --- a/libs/virtual_display/examples/virtual_display_1.rs +++ b/libs/virtual_display/examples/virtual_display_1.rs @@ -3,7 +3,7 @@ use virtual_display; fn prompt_input() -> u8 { println!("Press key execute:"); - println!(" 1. 'x' 1. exit"); + println!(" 1. 'q' 1. quit"); println!(" 2. 'i' 2. install or update driver"); println!(" 3. 'u' 3. uninstall driver"); println!(" 4. 'c' 4. create device"); @@ -18,18 +18,18 @@ fn prompt_input() -> u8 { .unwrap_or(0) } -fn plug_in() { +fn plug_in(monitor_index: u32) { println!("Plug in monitor begin"); - if let Err(e) = virtual_display::plug_in_monitor() { + if let Err(e) = virtual_display::plug_in_monitor(monitor_index as _) { println!("{}", e); } else { println!("Plug in monitor done"); } } -fn plug_out() { +fn plug_out(monitor_index: u32) { println!("Plug out monitor begin"); - if let Err(e) = virtual_display::plug_out_monitor() { + if let Err(e) = virtual_display::plug_out_monitor(monitor_index as _) { println!("{}", e); } else { println!("Plug out monitor done"); @@ -38,8 +38,9 @@ fn plug_out() { fn main() { loop { - match prompt_input() as char { - 'x' => break, + let chr = prompt_input(); + match chr as char { + 'q' => break, 'i' => { println!("Install or update driver begin"); let mut reboot_required = false; @@ -81,8 +82,12 @@ fn main() { virtual_display::close_device(); println!("Close device done"); } - '1' => plug_in(), - '4' => plug_out(), + '1' => plug_in(0), + '2' => plug_in(1), + '3' => plug_in(2), + '4' => plug_out(0), + '5' => plug_out(1), + '6' => plug_out(2), _ => {} } } diff --git a/libs/virtual_display/src/lib.rs b/libs/virtual_display/src/lib.rs index cd9402c69..578ffa2e9 100644 --- a/libs/virtual_display/src/lib.rs +++ b/libs/virtual_display/src/lib.rs @@ -1,12 +1,96 @@ -use hbb_common::{bail, ResultType}; -use std::sync::{Arc, Mutex}; +use hbb_common::{anyhow, dlopen::symbor::Library, log, ResultType}; +use std::{ + collections::HashSet, + sync::{Arc, Mutex}, +}; const LIB_NAME_VIRTUAL_DISPLAY: &str = "dylib_virtual_display"; +pub type DWORD = ::std::os::raw::c_ulong; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _MonitorMode { + pub width: DWORD, + pub height: DWORD, + pub sync: DWORD, +} +pub type MonitorMode = _MonitorMode; +pub type PMonitorMode = *mut MonitorMode; + +pub type GetDriverInstallPath = fn() -> &'static str; +pub type IsDeviceCreated = fn() -> bool; +pub type CloseDevice = fn(); +pub type DownLoadDriver = fn() -> ResultType<()>; +pub type CreateDevice = fn() -> ResultType<()>; +pub type InstallUpdateDriver = fn(&mut bool) -> ResultType<()>; +pub type UninstallDriver = fn(&mut bool) -> ResultType<()>; +pub type PlugInMonitor = fn(u32) -> ResultType<()>; +pub type PlugOutMonitor = fn(u32) -> ResultType<()>; +pub type UpdateMonitorModes = fn(u32, u32, PMonitorMode) -> ResultType<()>; + +macro_rules! make_lib_wrapper { + ($($field:ident : $tp:ty),+) => { + struct LibWrapper { + _lib: Option, + $($field: Option<$tp>),+ + } + + impl LibWrapper { + fn new() -> Self { + let lib = match Library::open(get_lib_name()) { + Ok(lib) => Some(lib), + Err(e) => { + log::warn!("Failed to load library {}, {}", LIB_NAME_VIRTUAL_DISPLAY, e); + None + } + }; + + $(let $field = if let Some(lib) = &lib { + match unsafe { lib.symbol::<$tp>(stringify!($field)) } { + Ok(m) => { + log::info!("method found {}", stringify!($field)); + Some(*m) + }, + Err(e) => { + log::warn!("Failed to load func {}, {}", stringify!($field), e); + None + } + } + } else { + None + };)+ + + Self { + _lib: lib, + $( $field ),+ + } + } + } + + impl Default for LibWrapper { + fn default() -> Self { + Self::new() + } + } + } +} + +make_lib_wrapper!( + get_driver_install_path: GetDriverInstallPath, + is_device_created: IsDeviceCreated, + close_device: CloseDevice, + download_driver: DownLoadDriver, + create_device: CreateDevice, + install_update_driver: InstallUpdateDriver, + uninstall_driver: UninstallDriver, + plug_in_monitor: PlugInMonitor, + plug_out_monitor: PlugOutMonitor, + update_monitor_modes: UpdateMonitorModes +); + lazy_static::lazy_static! { - static ref LIB_VIRTUAL_DISPLAY: Arc>> = { - Arc::new(Mutex::new(unsafe { libloading::Library::new(get_lib_name()) })) - }; + static ref LIB_WRAPPER: Arc> = Default::default(); + static ref MONITOR_INDICES: Mutex> = Mutex::new(HashSet::new()); } #[cfg(target_os = "windows")] @@ -24,102 +108,90 @@ fn get_lib_name() -> String { format!("lib{}.dylib", LIB_NAME_VIRTUAL_DISPLAY) } -fn try_reload_lib() { - let mut lock = LIB_VIRTUAL_DISPLAY.lock().unwrap(); - if lock.is_err() { - *lock = unsafe { libloading::Library::new(get_lib_name()) }; - } -} - #[cfg(windows)] -pub fn get_dirver_install_path() -> ResultType<&'static str> { - try_reload_lib(); - match &*LIB_VIRTUAL_DISPLAY.lock().unwrap() { - Ok(lib) => unsafe { - match lib.get:: &'static str>>(b"get_dirver_install_path") { - Ok(func) => Ok(func()), - Err(e) => bail!("Failed to load func get_dirver_install_path, {}", e), - } - }, - Err(e) => bail!("Failed to load library {}, {}", LIB_NAME_VIRTUAL_DISPLAY, e), - } +pub fn get_driver_install_path() -> Option<&'static str> { + Some(LIB_WRAPPER.lock().unwrap().get_driver_install_path?()) } pub fn is_device_created() -> bool { - try_reload_lib(); - match &*LIB_VIRTUAL_DISPLAY.lock().unwrap() { - Ok(lib) => unsafe { - match lib.get:: bool>>(b"is_device_created") { - Ok(func) => func(), - Err(..) => false, - } - }, - Err(..) => false, - } + LIB_WRAPPER + .lock() + .unwrap() + .is_device_created + .map(|f| f()) + .unwrap_or(false) } pub fn close_device() { - try_reload_lib(); - match &*LIB_VIRTUAL_DISPLAY.lock().unwrap() { - Ok(lib) => unsafe { - match lib.get::>(b"close_device") { - Ok(func) => func(), - Err(..) => {} - } - }, - Err(..) => {} - } + let _r = LIB_WRAPPER.lock().unwrap().close_device.map(|f| f()); } -macro_rules! def_func_result { - ($func:ident, $name: tt) => { - pub fn $func() -> ResultType<()> { - try_reload_lib(); - match &*LIB_VIRTUAL_DISPLAY.lock().unwrap() { - Ok(lib) => unsafe { - match lib.get:: ResultType<()>>>($name.as_bytes()) { - Ok(func) => func(), - Err(e) => bail!("Failed to load func {}, {}", $name, e), - } - }, - Err(e) => bail!("Failed to load library {}, {}", LIB_NAME_VIRTUAL_DISPLAY, e), - } - } - }; +pub fn download_driver() -> ResultType<()> { + LIB_WRAPPER + .lock() + .unwrap() + .download_driver + .ok_or(anyhow::Error::msg("download_driver method not found"))?() +} + +pub fn create_device() -> ResultType<()> { + LIB_WRAPPER + .lock() + .unwrap() + .create_device + .ok_or(anyhow::Error::msg("create_device method not found"))?() } pub fn install_update_driver(reboot_required: &mut bool) -> ResultType<()> { - try_reload_lib(); - match &*LIB_VIRTUAL_DISPLAY.lock().unwrap() { - Ok(lib) => unsafe { - match lib.get:: ResultType<()>>>( - b"install_update_driver", - ) { - Ok(func) => func(reboot_required), - Err(e) => bail!("Failed to load func install_update_driver, {}", e), - } - }, - Err(e) => bail!("Failed to load library {}, {}", LIB_NAME_VIRTUAL_DISPLAY, e), - } + LIB_WRAPPER + .lock() + .unwrap() + .install_update_driver + .ok_or(anyhow::Error::msg("install_update_driver method not found"))?(reboot_required) } pub fn uninstall_driver(reboot_required: &mut bool) -> ResultType<()> { - try_reload_lib(); - match &*LIB_VIRTUAL_DISPLAY.lock().unwrap() { - Ok(lib) => unsafe { - match lib - .get:: ResultType<()>>>(b"uninstall_driver") - { - Ok(func) => func(reboot_required), - Err(e) => bail!("Failed to load func uninstall_driver, {}", e), - } - }, - Err(e) => bail!("Failed to load library {}, {}", LIB_NAME_VIRTUAL_DISPLAY, e), - } + LIB_WRAPPER + .lock() + .unwrap() + .uninstall_driver + .ok_or(anyhow::Error::msg("uninstall_driver method not found"))?(reboot_required) } -def_func_result!(download_driver, "download_driver"); -def_func_result!(create_device, "create_device"); -def_func_result!(plug_in_monitor, "plug_in_monitor"); -def_func_result!(plug_out_monitor, "plug_out_monitor"); -def_func_result!(update_monitor_modes, "update_monitor_modes"); +#[cfg(windows)] +pub fn plug_in_monitor(monitor_index: u32) -> ResultType<()> { + let mut lock = MONITOR_INDICES.lock().unwrap(); + if lock.contains(&monitor_index) { + return Ok(()); + } + let f = LIB_WRAPPER + .lock() + .unwrap() + .plug_in_monitor + .ok_or(anyhow::Error::msg("plug_in_monitor method not found"))?; + f(monitor_index)?; + lock.insert(monitor_index); + Ok(()) +} + +#[cfg(windows)] +pub fn plug_out_monitor(monitor_index: u32) -> ResultType<()> { + let f = LIB_WRAPPER + .lock() + .unwrap() + .plug_out_monitor + .ok_or(anyhow::Error::msg("plug_out_monitor method not found"))?; + f(monitor_index)?; + MONITOR_INDICES.lock().unwrap().remove(&monitor_index); + Ok(()) +} + +#[cfg(windows)] +pub fn update_monitor_modes(monitor_index: u32, modes: &[MonitorMode]) -> ResultType<()> { + let f = LIB_WRAPPER + .lock() + .unwrap() + .update_monitor_modes + .ok_or(anyhow::Error::msg("update_monitor_modes method not found"))?; + f(monitor_index, modes.len() as _, modes.as_ptr() as _) +} diff --git a/src/flutter.rs b/src/flutter.rs index 6c9ff7f37..8c4522e47 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -3,18 +3,19 @@ use crate::{ flutter_ffi::EventToUI, ui_session_interface::{io_loop, InvokeUiSession, Session}, }; -#[cfg(feature = "flutter_texture_render")] -use dlopen::{ - symbor::{Library, Symbol}, - Error as LibError, -}; use flutter_rust_bridge::StreamSink; -#[cfg(feature = "flutter_texture_render")] -use hbb_common::libc::c_void; use hbb_common::{ bail, config::LocalConfig, get_version_number, log, message_proto::*, rendezvous_proto::ConnType, ResultType, }; +#[cfg(feature = "flutter_texture_render")] +use hbb_common::{ + dlopen::{ + symbor::{Library, Symbol}, + Error as LibError, + }, + libc::c_void, +}; use serde_json::json; #[cfg(not(feature = "flutter_texture_render"))] diff --git a/src/lib.rs b/src/lib.rs index 15c0ca037..d5f791eeb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,5 +66,9 @@ pub mod clipboard_file; #[cfg(all(windows, feature = "with_rc"))] pub mod rc; -#[cfg(target_os = "windows")] -pub mod win_privacy; + +#[cfg(windows)] +pub mod privacy_win_mag; + +#[cfg(all(windows, feature = "virtual_display_driver"))] +pub mod virtual_display_manager; diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 383259a32..b0b0d18e6 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -1,7 +1,10 @@ use super::{CursorData, ResultType}; use crate::common::PORTABLE_APPNAME_RUNTIME_ENV_KEY; -use crate::ipc; -use crate::license::*; +use crate::{ + ipc, + license::*, + privacy_win_mag::{self, WIN_MAG_INJECTED_PROCESS_EXE}, +}; use hbb_common::{ allow_err, bail, config::{self, Config}, @@ -838,8 +841,8 @@ fn get_default_install_path() -> String { pub fn check_update_broker_process() -> ResultType<()> { // let (_, path, _, _) = get_install_info(); - let process_exe = crate::win_privacy::INJECTED_PROCESS_EXE; - let origin_process_exe = crate::win_privacy::ORIGIN_PROCESS_EXE; + let process_exe = privacy_win_mag::INJECTED_PROCESS_EXE; + let origin_process_exe = privacy_win_mag::ORIGIN_PROCESS_EXE; let exe_file = std::env::current_exe()?; if exe_file.parent().is_none() { @@ -921,8 +924,7 @@ pub fn copy_raw_cmd(src_raw: &str, _raw: &str, _path: &str) -> String { pub fn copy_exe_cmd(src_exe: &str, exe: &str, path: &str) -> String { let main_exe = copy_raw_cmd(src_exe, exe, path); - - return format!( + format!( " {main_exe} copy /Y \"{ORIGIN_PROCESS_EXE}\" \"{path}\\{broker_exe}\" @@ -930,9 +932,9 @@ pub fn copy_exe_cmd(src_exe: &str, exe: &str, path: &str) -> String { ", main_exe = main_exe, path = path, - ORIGIN_PROCESS_EXE = crate::win_privacy::ORIGIN_PROCESS_EXE, - broker_exe = crate::win_privacy::INJECTED_PROCESS_EXE, - ); + ORIGIN_PROCESS_EXE = privacy_win_mag::ORIGIN_PROCESS_EXE, + broker_exe = privacy_win_mag::INJECTED_PROCESS_EXE, + ) } pub fn update_me() -> ResultType<()> { @@ -963,7 +965,7 @@ pub fn update_me() -> ResultType<()> { ", copy_exe = copy_exe_cmd(&src_exe, &exe, &path), copy_dll = copy_dll, - broker_exe = crate::win_privacy::INJECTED_PROCESS_EXE, + broker_exe = WIN_MAG_INJECTED_PROCESS_EXE, app_name = crate::get_app_name(), lic = register_licence(), cur_pid = get_current_pid(), @@ -1257,7 +1259,7 @@ fn get_before_uninstall(kill_self: bool) -> String { netsh advfirewall firewall delete rule name=\"{app_name} Service\" ", app_name = app_name, - broker_exe = crate::win_privacy::INJECTED_PROCESS_EXE, + broker_exe = WIN_MAG_INJECTED_PROCESS_EXE, ext = ext, filter = filter, ) @@ -2180,6 +2182,14 @@ pub fn get_unicode_from_vk(vk: u32) -> Option { } } +pub fn is_process_consent_running() -> ResultType { + let output = std::process::Command::new("cmd") + .args(&["/C", "tasklist | findstr consent.exe"]) + .creation_flags(CREATE_NO_WINDOW) + .output()?; + Ok(output.status.success() && !output.stdout.is_empty()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/win_privacy.rs b/src/privacy_win_mag.rs similarity index 96% rename from src/win_privacy.rs rename to src/privacy_win_mag.rs index 9944bf262..fe0ee4f69 100644 --- a/src/win_privacy.rs +++ b/src/privacy_win_mag.rs @@ -5,7 +5,6 @@ use crate::{ use hbb_common::{allow_err, bail, lazy_static, log, tokio, ResultType}; use std::{ ffi::CString, - os::windows::process::CommandExt, sync::Mutex, time::{Duration, Instant}, }; @@ -25,18 +24,22 @@ use winapi::{ CreateProcessAsUserW, GetCurrentThreadId, QueueUserAPC, ResumeThread, PROCESS_INFORMATION, STARTUPINFOW, }, - winbase::{ - WTSGetActiveConsoleSessionId, CREATE_NO_WINDOW, CREATE_SUSPENDED, DETACHED_PROCESS, - }, + winbase::{WTSGetActiveConsoleSessionId, CREATE_SUSPENDED, DETACHED_PROCESS}, winnt::{MEM_COMMIT, PAGE_READWRITE}, winuser::*, }, }; pub const ORIGIN_PROCESS_EXE: &'static str = "C:\\Windows\\System32\\RuntimeBroker.exe"; -pub const INJECTED_PROCESS_EXE: &'static str = "RuntimeBroker_rustdesk.exe"; +pub const WIN_MAG_INJECTED_PROCESS_EXE: &'static str = "RuntimeBroker_rustdesk.exe"; +pub const INJECTED_PROCESS_EXE: &'static str = WIN_MAG_INJECTED_PROCESS_EXE; pub const PRIVACY_WINDOW_NAME: &'static str = "RustDeskPrivacyWindow"; +pub const OCCUPIED: &'static str = "Privacy occupied by another one"; +pub const TURN_OFF_OTHER_ID: &'static str = + "Failed to turn off privacy mode that belongs to someone else"; +pub const NO_DISPLAYS: &'static str = "No displays"; + pub const GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT: u32 = 2; pub const GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS: u32 = 4; @@ -317,14 +320,6 @@ fn wait_find_privacy_hwnd(msecs: u128) -> ResultType { } } -pub fn is_process_consent_running() -> ResultType { - let output = std::process::Command::new("cmd") - .args(&["/C", "tasklist | findstr consent.exe"]) - .creation_flags(CREATE_NO_WINDOW) - .output()?; - Ok(output.status.success() && !output.stdout.is_empty()) -} - #[tokio::main(flavor = "current_thread")] async fn set_privacy_mode_state( conn_id: i32, diff --git a/src/server/connection.rs b/src/server/connection.rs index 8a6ba99b9..d4b645078 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -2346,13 +2346,13 @@ fn try_activate_screen() { mod privacy_mode { use super::*; + #[cfg(windows)] + use crate::privacy_win_mag; pub(super) fn turn_off_privacy(_conn_id: i32) -> Message { #[cfg(windows)] { - use crate::win_privacy::*; - - let res = turn_off_privacy(_conn_id, None); + let res = privacy_win_mag::turn_off_privacy(_conn_id, None); match res { Ok(_) => crate::common::make_privacy_mode_msg( back_notification::PrivacyModeState::PrvOffSucceeded, @@ -2374,7 +2374,7 @@ mod privacy_mode { pub(super) fn turn_on_privacy(_conn_id: i32) -> ResultType { #[cfg(windows)] { - let plugin_exist = crate::win_privacy::turn_on_privacy(_conn_id)?; + let plugin_exist = privacy_win_mag::turn_on_privacy(_conn_id)?; Ok(plugin_exist) } #[cfg(not(windows))] diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 4abeafff5..e6f29a405 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -19,6 +19,10 @@ // https://slhck.info/video/2017/03/01/rate-control.html use super::{video_qos::VideoQoS, *}; +#[cfg(all(windows, feature = "virtual_display_driver"))] +use crate::virtual_display_manager; +#[cfg(windows)] +use crate::{platform::windows::is_process_consent_running, privacy_win_mag}; #[cfg(windows)] use hbb_common::get_version_number; use hbb_common::tokio::sync::{ @@ -41,8 +45,6 @@ use std::{ ops::{Deref, DerefMut}, time::{self, Duration, Instant}, }; -#[cfg(windows)] -use virtual_display; pub const SCRAP_UBUNTU_HIGHER_REQUIRED: &str = "Wayland requires Ubuntu 21.04 or higher version."; pub const SCRAP_OTHER_VERSION_OR_X11_REQUIRED: &str = @@ -208,8 +210,6 @@ fn create_capturer( if privacy_mode_id > 0 { #[cfg(windows)] { - use crate::win_privacy::*; - match scrap::CapturerMag::new( display.origin(), display.width(), @@ -220,7 +220,7 @@ fn create_capturer( let mut ok = false; let check_begin = Instant::now(); while check_begin.elapsed().as_secs() < 5 { - match c1.exclude("", PRIVACY_WINDOW_NAME) { + match c1.exclude("", privacy_win_mag::PRIVACY_WINDOW_NAME) { Ok(false) => { ok = false; std::thread::sleep(std::time::Duration::from_millis(500)); @@ -229,7 +229,7 @@ fn create_capturer( bail!( "Failed to exclude privacy window {} - {}, err: {}", "", - PRIVACY_WINDOW_NAME, + privacy_win_mag::PRIVACY_WINDOW_NAME, e ); } @@ -243,7 +243,7 @@ fn create_capturer( bail!( "Failed to exclude privacy window {} - {} ", "", - PRIVACY_WINDOW_NAME + privacy_win_mag::PRIVACY_WINDOW_NAME ); } log::debug!("Create magnifier capture for {}", privacy_mode_id); @@ -275,18 +275,12 @@ fn create_capturer( }; } -#[cfg(windows)] +// to-do: do not close if in privacy mode. +#[cfg(all(windows, feature = "virtual_display_driver"))] fn ensure_close_virtual_device() -> ResultType<()> { let num_displays = Display::all()?.len(); - if num_displays == 0 { - // Device may sometimes be uninstalled by user in "Device Manager" Window. - // Closing device will clear the instance data. - virtual_display::close_device(); - } else if num_displays > 1 { - // Try close device, if display device changed. - if virtual_display::is_device_created() { - virtual_display::close_device(); - } + if num_displays > 1 { + virtual_display_manager::plug_out_headless(); } Ok(()) } @@ -309,11 +303,11 @@ pub fn test_create_capturer(privacy_mode_id: i32, timeout_millis: u64) -> bool { fn check_uac_switch(privacy_mode_id: i32, capturer_privacy_mode_id: i32) -> ResultType<()> { if capturer_privacy_mode_id != 0 { if privacy_mode_id != capturer_privacy_mode_id { - if !crate::win_privacy::is_process_consent_running()? { + if !is_process_consent_running()? { bail!("consent.exe is running"); } } - if crate::win_privacy::is_process_consent_running()? { + if is_process_consent_running()? { bail!("consent.exe is running"); } } @@ -374,7 +368,7 @@ fn get_capturer(use_yuv: bool, portable_service_running: bool) -> ResultType Option { } fn run(sp: GenericService) -> ResultType<()> { - #[cfg(windows)] + #[cfg(all(windows, feature = "virtual_display_driver"))] ensure_close_virtual_device()?; // ensure_inited() is needed because release_resource() may be called. @@ -922,37 +916,24 @@ pub async fn switch_to_primary() { } #[inline] -#[cfg(not(windows))] +#[cfg(not(all(windows, feature = "virtual_display_driver")))] fn try_get_displays() -> ResultType> { Ok(Display::all()?) } -#[cfg(windows)] +#[cfg(all(windows, feature = "virtual_display_driver"))] fn try_get_displays() -> ResultType> { let mut displays = Display::all()?; if displays.len() == 0 { log::debug!("no displays, create virtual display"); - // Try plugin monitor - if !virtual_display::is_device_created() { - if let Err(e) = virtual_display::create_device() { - log::debug!("Create device failed {}", e); - } + if let Err(e) = virtual_display_manager::plug_in_headless() { + log::error!("plug in headless failed {}", e); + } else { + displays = Display::all()?; } - if virtual_display::is_device_created() { - if let Err(e) = virtual_display::plug_in_monitor() { - log::debug!("Plug in monitor failed {}", e); - } else { - if let Err(e) = virtual_display::update_monitor_modes() { - log::debug!("Update monitor modes failed {}", e); - } - } - } - displays = Display::all()?; } else if displays.len() > 1 { // If more than one displays exists, close RustDeskVirtualDisplay - if virtual_display::is_device_created() { - virtual_display::close_device() - } + let _res = virtual_display_manager::plug_in_headless(); } Ok(displays) } @@ -991,7 +972,7 @@ fn start_uac_elevation_check() { if !crate::platform::is_installed() && !crate::platform::is_root() { std::thread::spawn(|| loop { std::thread::sleep(std::time::Duration::from_secs(1)); - if let Ok(uac) = crate::win_privacy::is_process_consent_running() { + if let Ok(uac) = is_process_consent_running() { *IS_UAC_RUNNING.lock().unwrap() = uac; } if !crate::platform::is_elevated(None).unwrap_or(false) { diff --git a/src/ui_cm_interface.rs b/src/ui_cm_interface.rs index 8612b5751..b6bb3e35d 100644 --- a/src/ui_cm_interface.rs +++ b/src/ui_cm_interface.rs @@ -504,7 +504,7 @@ pub async fn start_ipc(cm: ConnectionManager) { e ); } - allow_err!(crate::win_privacy::start()); + allow_err!(crate::privacy_win_mag::start()); }); match ipc::new_listener("_cm").await { diff --git a/src/virtual_display_manager.rs b/src/virtual_display_manager.rs new file mode 100644 index 000000000..82328915e --- /dev/null +++ b/src/virtual_display_manager.rs @@ -0,0 +1,106 @@ +use hbb_common::{allow_err, bail, lazy_static, log, ResultType}; +use std::{ + collections::HashSet, + sync::{Arc, Mutex}, +}; + +// virtual display index range: 0 - 2 are reserved for headless and other special uses. +const VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS: u32 = 0; +const VIRTUAL_DISPLAY_START_FOR_PEER: u32 = 3; +const VIRTUAL_DISPLAY_MAX_COUNT: u32 = 10; + +lazy_static::lazy_static! { + static ref VIRTUAL_DISPLAY_MANAGER: Arc> = + Arc::new(Mutex::new(VirtualDisplayManager::default())); +} + +#[derive(Default)] +struct VirtualDisplayManager { + headless_index: Option, + peer_required_indices: HashSet, +} + +impl VirtualDisplayManager { + fn prepare_driver() -> ResultType<()> { + if let Err(e) = virtual_display::create_device() { + if !e.to_string().contains("Device is already created") { + bail!("Create device failed {}", e); + } + } + // Reboot is not required for this case. + let mut _reboot_required = false; + allow_err!(virtual_display::install_update_driver( + &mut _reboot_required + )); + Ok(()) + } + + fn plug_in_monitor(index: u32, modes: &[virtual_display::MonitorMode]) -> ResultType<()> { + if let Err(e) = virtual_display::plug_in_monitor(index) { + bail!("Plug in monitor failed {}", e); + } + if let Err(e) = virtual_display::update_monitor_modes(index, &modes) { + log::error!("Update monitor modes failed {}", e); + } + Ok(()) + } +} + +pub fn plug_in_headless() -> ResultType<()> { + let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); + VirtualDisplayManager::prepare_driver()?; + let modes = [virtual_display::MonitorMode { + width: 1920, + height: 1080, + sync: 60, + }]; + VirtualDisplayManager::plug_in_monitor(VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS, &modes)?; + manager.headless_index = Some(VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS); + Ok(()) +} + +pub fn plug_out_headless() { + let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); + if let Some(index) = manager.headless_index.take() { + if let Err(e) = virtual_display::plug_out_monitor(index) { + log::error!("Plug out monitor failed {}", e); + } + } +} + +pub fn plug_in_peer_required( + 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) { + match VirtualDisplayManager::plug_in_monitor(idx, m) { + Ok(_) => { + manager.peer_required_indices.insert(idx); + indices.push(idx); + } + Err(e) => { + log::error!("Plug in monitor failed {}", e); + } + } + } + } + } + + Ok(indices) +} + +pub fn plug_out_peer_required(modes: &[u32]) -> ResultType<()> { + let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); + for idx in modes.iter() { + if manager.peer_required_indices.contains(idx) { + allow_err!(virtual_display::plug_out_monitor(*idx)); + manager.peer_required_indices.remove(idx); + } + } + Ok(()) +}