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 f1c39a64d..94ef44e0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,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 } 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/config.rs b/libs/hbb_common/src/config.rs index 369920982..8786858e2 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -1141,6 +1141,8 @@ pub struct LocalConfig { // Various data for flutter ui #[serde(default)] ui_flutter: HashMap, + #[serde(default)] + virtual_display_num: usize, } impl LocalConfig { @@ -1243,6 +1245,19 @@ impl LocalConfig { config.store(); } } + + pub fn get_virtual_display_num() -> usize { + LOCAL_CONFIG.read().unwrap().virtual_display_num + } + + pub fn set_virtual_display_num(virtual_display_num: usize) { + let mut lock = LOCAL_CONFIG.write().unwrap(); + if lock.virtual_display_num == virtual_display_num { + return; + } + lock.virtual_display_num = virtual_display_num; + lock.store(); + } } #[derive(Debug, Default, Serialize, Deserialize, Clone)] 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..2c2372212 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" } 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/examples/virtual_display_1.rs b/libs/virtual_display/examples/virtual_display_1.rs index 31fdbe06e..1471e3faf 100644 --- a/libs/virtual_display/examples/virtual_display_1.rs +++ b/libs/virtual_display/examples/virtual_display_1.rs @@ -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,7 +38,8 @@ fn plug_out() { fn main() { loop { - match prompt_input() as char { + let chr = prompt_input(); + match chr as char { 'x' => break, 'i' => { println!("Install or update driver begin"); @@ -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..f7e904537 100644 --- a/libs/virtual_display/src/lib.rs +++ b/libs/virtual_display/src/lib.rs @@ -1,12 +1,93 @@ -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) => 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 +105,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/server/video_service.rs b/src/server/video_service.rs index e6a4e3806..e5394157d 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -23,6 +23,8 @@ use super::{video_qos::VideoQoS, *}; use crate::platform::windows::is_process_consent_running; #[cfg(all(windows, feature = "privacy_win_mag"))] use crate::privacy_mode::privacy_win_mag; +#[cfg(all(windows, feature = "virtual_display_driver"))] +use hbb_common::config::LocalConfig; #[cfg(all(windows, feature = "privacy_win_mag"))] use hbb_common::get_version_number; use hbb_common::tokio::sync::{ @@ -48,6 +50,9 @@ use std::{ #[cfg(all(windows, feature = "virtual_display_driver"))] use virtual_display; +#[cfg(all(windows, feature = "virtual_display_driver"))] +const VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS: u32 = 0; + pub const SCRAP_UBUNTU_HIGHER_REQUIRED: &str = "Wayland requires Ubuntu 21.04 or higher version."; pub const SCRAP_OTHER_VERSION_OR_X11_REQUIRED: &str = "Wayland requires higher version of linux distro. Please try X11 desktop or change your OS."; @@ -936,21 +941,20 @@ fn try_get_displays() -> ResultType> { 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 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); + if LocalConfig::get_virtual_display_num() > 0 { + if !virtual_display::is_device_created() { + if let Err(e) = virtual_display::create_device() { + log::debug!("Create device failed {}", e); } } + if virtual_display::is_device_created() { + if let Err(e) = virtual_display::plug_in_monitor(VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS) + { + log::debug!("Plug in monitor failed {}", e); + } + } + displays = Display::all()?; } - displays = Display::all()?; } else if displays.len() > 1 { // to-do: do not close if in privacy mode.