diff --git a/Cargo.lock b/Cargo.lock index 4f1d53cd0..33bc04259 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3444,6 +3444,7 @@ dependencies = [ "sysinfo", "systray", "uuid", + "virtual_display", "whoami", "winapi 0.3.9", "windows-service", @@ -4180,6 +4181,18 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "virtual_display" +version = "0.1.0" +dependencies = [ + "cc", + "hbb_common", + "lazy_static", + "serde 1.0.136", + "serde_derive", + "thiserror", +] + [[package]] name = "walkdir" version = "2.3.2" diff --git a/Cargo.toml b/Cargo.toml index 7cab6e7a6..7df6eca3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ scrap = { path = "libs/scrap" } hbb_common = { path = "libs/hbb_common" } enigo = { path = "libs/enigo" } clipboard = { path = "libs/clipboard" } +virtual_display = { path = "libs/virtual_display" } sys-locale = "0.1" serde_derive = "1.0" serde = "1.0" @@ -83,7 +84,7 @@ rust-pulsectl = { git = "https://github.com/open-trade/pulsectl" } android_logger = "0.10" [workspace] -members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard"] +members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display"] [package.metadata.winres] LegalCopyright = "Copyright © 2020" diff --git a/libs/hbb_common/src/lib.rs b/libs/hbb_common/src/lib.rs index e66eed7d0..81871bf26 100644 --- a/libs/hbb_common/src/lib.rs +++ b/libs/hbb_common/src/lib.rs @@ -27,13 +27,14 @@ pub use anyhow::{self, bail}; pub use futures_util; pub mod config; pub mod fs; +pub use lazy_static; +pub use mac_address; pub use rand; pub use regex; pub use sodiumoxide; pub use tokio_socks; pub use tokio_socks::IntoTargetAddr; pub use tokio_socks::TargetAddr; -pub use mac_address; #[cfg(feature = "quic")] pub type Stream = quic::Connection; diff --git a/libs/virtual_display/Cargo.toml b/libs/virtual_display/Cargo.toml new file mode 100644 index 000000000..8d0b65171 --- /dev/null +++ b/libs/virtual_display/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "virtual_display" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[build-dependencies] +cc = "1.0" + +[dependencies] +thiserror = "1.0.30" +lazy_static = "1.4" +serde = "1.0" +serde_derive = "1.0" +hbb_common = { path = "../hbb_common" } diff --git a/libs/virtual_display/README.md b/libs/virtual_display/README.md new file mode 100644 index 000000000..d5a1a0862 --- /dev/null +++ b/libs/virtual_display/README.md @@ -0,0 +1,32 @@ +# virtual display + +Virtual display may be used on computers that do not have a monitor. + +[Development reference](https://github.com/pavlobu/deskreen/discussions/86) + +## windows + +### win10 + +Win10 provides [Indirect Display Driver Model](https://msdn.microsoft.com/en-us/library/windows/hardware/mt761968(v=vs.85).aspx). + +This lib uses [this project](https://github.com/fufesou/RustDeskIddDriver) as the driver. + + +**NOTE**: Versions before Win10 1607. Try follow [this method](https://github.com/fanxiushu/xdisp_virt/tree/master/indirect_display). + + +#### tested platforms + +- [x] 19041 +- [x] 19043 + +### win7 + +TODO + +[WDDM](https://docs.microsoft.com/en-us/windows-hardware/drivers/display/windows-vista-display-driver-model-design-guide). + +## X11 + +## OSX diff --git a/libs/virtual_display/build.rs b/libs/virtual_display/build.rs new file mode 100644 index 000000000..177d92371 --- /dev/null +++ b/libs/virtual_display/build.rs @@ -0,0 +1,35 @@ +use cc; + +fn build_c_impl() { + let mut build = cc::Build::new(); + + #[cfg(target_os = "windows")] + build.file("src/win10/IddController.c"); + + build.flag_if_supported("-Wno-c++0x-extensions"); + build.flag_if_supported("-Wno-return-type-c-linkage"); + build.flag_if_supported("-Wno-invalid-offsetof"); + build.flag_if_supported("-Wno-unused-parameter"); + + if build.get_compiler().is_like_msvc() { + build.define("WIN32", ""); + build.flag("-Zi"); + build.flag("-GR-"); + // build.flag("-std:c++11"); + } else { + build.flag("-fPIC"); + // build.flag("-std=c++11"); + // build.flag("-include"); + // build.flag(&confdefs_path.to_string_lossy()); + } + + #[cfg(target_os = "windows")] + build.compile("xxx"); + + #[cfg(target_os = "windows")] + println!("cargo:rerun-if-changed=src/win10/IddController.c"); +} + +fn main() { + build_c_impl(); +} diff --git a/libs/virtual_display/examples/idd_controller.rs b/libs/virtual_display/examples/idd_controller.rs new file mode 100644 index 000000000..98e2c2b44 --- /dev/null +++ b/libs/virtual_display/examples/idd_controller.rs @@ -0,0 +1,138 @@ +#[cfg(windows)] +use virtual_display::win10::{idd, DRIVER_INSTALL_PATH}; + +use std::{ + ffi::{CStr, CString}, + io::{self, Read}, + path::Path, +}; + +fn prompt_input() -> u8 { + println!("Press key execute:"); + println!(" 1. 'x' 1. exit"); + println!(" 2. 'i' 2. install or update driver"); + println!(" 3. 'u' 3. uninstall driver"); + println!(" 4. 'c' 4. create device"); + println!(" 5. 'd' 5. destroy device"); + println!(" 6. '1','2','3' 6. plug in monitor 0,1,2"); + println!(" 7. '4','5','6' 7. plug out monitor 0,1,2"); + + io::stdin() + .bytes() + .next() + .and_then(|result| result.ok()) + .unwrap_or(0) +} + +unsafe fn plug_in(index: idd::UINT, edid: idd::UINT) { + println!("Plug in monitor begin"); + if idd::FALSE == idd::MonitorPlugIn(index, edid, 25) { + println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap()); + } else { + println!("Plug in monitor done"); + + let mut modes: Vec = Vec::new(); + modes.push(idd::MonitorMode { + width: 1920 as idd::DWORD, + height: 1080 as idd::DWORD, + sync: 60 as idd::DWORD, + }); + modes.push(idd::MonitorMode { + width: 1024 as idd::DWORD, + height: 768 as idd::DWORD, + sync: 60 as idd::DWORD, + }); + if idd::FALSE == idd::MonitorModesUpdate(index, modes.len() as u32, modes.as_mut_ptr()) { + println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap()); + } + } +} + +unsafe fn plug_out(index: idd::UINT) { + println!("Plug out monitor begin"); + if idd::FALSE == idd::MonitorPlugOut(index) { + println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap()); + } else { + println!("Plug out monitor done"); + } +} + +fn main() { + let abs_path = Path::new(DRIVER_INSTALL_PATH).canonicalize().unwrap(); + let full_inf_path = abs_path.to_str().unwrap(); + + unsafe { + let invalid_device = 0 as idd::HSWDEVICE; + let mut h_sw_device = invalid_device; + let full_inf_path = CString::new(full_inf_path).unwrap().into_raw(); + loop { + match prompt_input() as char { + 'x' => break, + 'i' => { + println!("Install or update driver begin"); + let mut reboot_required = idd::FALSE; + if idd::InstallUpdate(full_inf_path, &mut reboot_required) == idd::FALSE { + println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap()); + } else { + println!( + "Install or update driver done, reboot is {} required", + if reboot_required == idd::FALSE { + "not" + } else { + "" + } + ); + } + } + 'u' => { + println!("Uninstall driver begin"); + let mut reboot_required = idd::FALSE; + if idd::Uninstall(full_inf_path, &mut reboot_required) == idd::FALSE { + println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap()); + } else { + println!( + "Uninstall driver done, reboot is {} required", + if reboot_required == idd::FALSE { + "not" + } else { + "" + } + ); + } + } + 'c' => { + println!("Create device begin"); + if h_sw_device != invalid_device { + println!("Device created before"); + continue; + } + if idd::FALSE == idd::DeviceCreate(&mut h_sw_device) { + println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap()); + idd::DeviceClose(h_sw_device); + h_sw_device = invalid_device; + } else { + println!("Create device done"); + } + } + 'd' => { + println!("Close device begin"); + idd::DeviceClose(h_sw_device); + h_sw_device = invalid_device; + println!("Close device done"); + } + '1' => plug_in(0, 0), + '2' => plug_in(1, 0), + '3' => plug_in(2, 0), + '4' => plug_out(0), + '5' => plug_out(1), + '6' => plug_out(2), + _ => {} + } + } + if !full_inf_path.is_null() { + let _ = CString::from_raw(full_inf_path); + } + + idd::DeviceClose(h_sw_device); + } +} diff --git a/libs/virtual_display/examples/virtual_display_1.rs b/libs/virtual_display/examples/virtual_display_1.rs new file mode 100644 index 000000000..31fdbe06e --- /dev/null +++ b/libs/virtual_display/examples/virtual_display_1.rs @@ -0,0 +1,89 @@ +use std::io::{self, Read}; +use virtual_display; + +fn prompt_input() -> u8 { + println!("Press key execute:"); + println!(" 1. 'x' 1. exit"); + println!(" 2. 'i' 2. install or update driver"); + println!(" 3. 'u' 3. uninstall driver"); + println!(" 4. 'c' 4. create device"); + println!(" 5. 'd' 5. destroy device"); + println!(" 6. '1' 6. plug in monitor 0,1,2"); + println!(" 7. '4' 7. plug out monitor 0,1,2"); + + io::stdin() + .bytes() + .next() + .and_then(|result| result.ok()) + .unwrap_or(0) +} + +fn plug_in() { + println!("Plug in monitor begin"); + if let Err(e) = virtual_display::plug_in_monitor() { + println!("{}", e); + } else { + println!("Plug in monitor done"); + } +} + +fn plug_out() { + println!("Plug out monitor begin"); + if let Err(e) = virtual_display::plug_out_monitor() { + println!("{}", e); + } else { + println!("Plug out monitor done"); + } +} + +fn main() { + loop { + match prompt_input() as char { + 'x' => break, + 'i' => { + println!("Install or update driver begin"); + let mut reboot_required = false; + if let Err(e) = virtual_display::install_update_driver(&mut reboot_required) { + println!("{}", e); + } else { + println!( + "Install or update driver done, reboot is {} required", + if reboot_required { "" } else { "not" } + ); + } + } + 'u' => { + println!("Uninstall driver begin"); + let mut reboot_required = false; + if let Err(e) = virtual_display::uninstall_driver(&mut reboot_required) { + println!("{}", e); + } else { + println!( + "Uninstall driver done, reboot is {} required", + if reboot_required { "" } else { "not" } + ); + } + } + 'c' => { + println!("Create device begin"); + if virtual_display::is_device_created() { + println!("Device created before"); + continue; + } + if let Err(e) = virtual_display::create_device() { + println!("{}", e); + } else { + println!("Create device done"); + } + } + 'd' => { + println!("Close device begin"); + virtual_display::close_device(); + println!("Close device done"); + } + '1' => plug_in(), + '4' => plug_out(), + _ => {} + } + } +} diff --git a/libs/virtual_display/src/lib.rs b/libs/virtual_display/src/lib.rs new file mode 100644 index 000000000..73b4bdbd4 --- /dev/null +++ b/libs/virtual_display/src/lib.rs @@ -0,0 +1,182 @@ +#[cfg(windows)] +pub mod win10; + +use hbb_common::{bail, lazy_static, ResultType}; +use std::{ffi::CString, path::Path, sync::Mutex}; + +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()); +} + +pub fn download_driver() -> ResultType<()> { + #[cfg(windows)] + let _download_url = win10::DRIVER_DOWNLOAD_URL; + #[cfg(target_os = "linux")] + let _download_url = ""; + + // process download and report progress + + Ok(()) +} + +pub fn install_update_driver(_reboot_required: &mut bool) -> ResultType<()> { + #[cfg(windows)] + let install_path = win10::DRIVER_INSTALL_PATH; + #[cfg(not(windows))] + let install_path = ""; + + let abs_path = Path::new(install_path).canonicalize()?; + if !abs_path.exists() { + bail!("{} not exists", install_path) + } + + let _full_install_path = match abs_path.to_str() { + Some(p) => CString::new(p)?.into_raw(), + None => bail!("{} not exists", install_path), + }; + + #[cfg(windows)] + unsafe { + { + let mut reboot_required_tmp = win10::idd::FALSE; + if win10::idd::InstallUpdate(_full_install_path, &mut reboot_required_tmp) + == win10::idd::FALSE + { + bail!("{}", win10::get_last_msg()?); + } + *_reboot_required = reboot_required_tmp == win10::idd::TRUE; + } + } + + Ok(()) +} + +pub fn uninstall_driver(_reboot_required: &mut bool) -> ResultType<()> { + #[cfg(windows)] + let install_path = win10::DRIVER_INSTALL_PATH; + #[cfg(not(windows))] + let install_path = ""; + + let abs_path = Path::new(install_path).canonicalize()?; + if !abs_path.exists() { + bail!("{} not exists", install_path) + } + + let _full_install_path = match abs_path.to_str() { + Some(p) => CString::new(p)?.into_raw(), + None => bail!("{} not exists", install_path), + }; + + #[cfg(windows)] + unsafe { + { + let mut reboot_required_tmp = win10::idd::FALSE; + if win10::idd::Uninstall(_full_install_path, &mut reboot_required_tmp) + == win10::idd::FALSE + { + bail!("{}", win10::get_last_msg()?); + } + *_reboot_required = reboot_required_tmp == win10::idd::TRUE; + } + } + + Ok(()) +} + +pub fn is_device_created() -> bool { + #[cfg(windows)] + return *H_SW_DEVICE.lock().unwrap() != 0; + #[cfg(not(windows))] + return false; +} + +pub fn create_device() -> ResultType<()> { + if is_device_created() { + return Ok(()); + } + #[cfg(windows)] + unsafe { + let mut lock_device = H_SW_DEVICE.lock().unwrap(); + let mut h_sw_device = *lock_device as win10::idd::HSWDEVICE; + if win10::idd::DeviceCreate(&mut h_sw_device) == win10::idd::FALSE { + bail!("{}", win10::get_last_msg()?); + } else { + *lock_device = h_sw_device as u64; + } + } + Ok(()) +} + +pub fn close_device() { + #[cfg(windows)] + unsafe { + win10::idd::DeviceClose(*H_SW_DEVICE.lock().unwrap() as win10::idd::HSWDEVICE); + *H_SW_DEVICE.lock().unwrap() = 0; + MONITOR_PLUGIN.lock().unwrap().clear(); + } +} + +pub fn plug_in_monitor() -> 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(()) +} + +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(()) +} + +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(), + ) + { + bail!("{}", win10::get_last_msg()?); + } + } + Ok(()) +} diff --git a/libs/virtual_display/src/win10/IddController.c b/libs/virtual_display/src/win10/IddController.c new file mode 100644 index 000000000..dd18a622d --- /dev/null +++ b/libs/virtual_display/src/win10/IddController.c @@ -0,0 +1,784 @@ +#include "./IddController.h" +#include +#include +#include +#include +#include +#include +#include + +#include "./Public.h" + + +const GUID GUID_DEVINTERFACE_IDD_DRIVER_DEVICE = \ +{ 0x781EF630, 0x72B2, 0x11d2, { 0xB8, 0x52, 0x00, 0xC0, 0x4E, 0xAF, 0x52, 0x72 } }; +//{781EF630-72B2-11d2-B852-00C04EAF5272} + +BOOL g_printMsg = TRUE; +char g_lastMsg[1024]; +const char* g_msgHeader = "RustDeskIdd: "; + +VOID WINAPI +CreationCallback( + _In_ HSWDEVICE hSwDevice, + _In_ HRESULT hrCreateResult, + _In_opt_ PVOID pContext, + _In_opt_ PCWSTR pszDeviceInstanceId +); +// https://github.com/microsoft/Windows-driver-samples/blob/9f03207ae1e8df83325f067de84494ae55ab5e97/general/DCHU/osrfx2_DCHU_base/osrfx2_DCHU_testapp/testapp.c#L88 +// Not a good way for this device, I don't not why. I'm not familiar with dirver. +BOOLEAN GetDevicePath( + _In_ LPCGUID InterfaceGuid, + _Out_writes_(BufLen) PTCHAR DevicePath, + _In_ size_t BufLen +); +// https://github.com/microsoft/Windows-driver-samples/blob/9f03207ae1e8df83325f067de84494ae55ab5e97/usb/umdf_fx2/exe/testapp.c#L90 +// Works good to check whether device is created before. +BOOLEAN GetDevicePath2( + _In_ LPCGUID InterfaceGuid, + _Out_writes_(BufLen) PTCHAR DevicePath, + _In_ size_t BufLen +); + +HANDLE DeviceOpenHandle(); +VOID DeviceCloseHandle(HANDLE handle); + +void SetLastMsg(const char* format, ...) +{ + memset(g_lastMsg, 0, sizeof(g_lastMsg)); + memcpy_s(g_lastMsg, sizeof(g_lastMsg), g_msgHeader, strlen(g_msgHeader)); + + va_list args; + va_start(args, format); + vsnprintf_s( + g_lastMsg + strlen(g_msgHeader), + sizeof(g_lastMsg) - strlen(g_msgHeader), + _TRUNCATE, + format, + args); + va_end(args); +} + +const char* GetLastMsg() +{ + return g_lastMsg; +} + +BOOL InstallUpdate(LPCTSTR fullInfPath, PBOOL rebootRequired) +{ + SetLastMsg("Sucess"); + + // UpdateDriverForPlugAndPlayDevices may return FALSE while driver was successfully installed... + if (FALSE == UpdateDriverForPlugAndPlayDevices( + NULL, + _T("RustDeskIddDriver"), // match hardware id in the inf file + fullInfPath, + INSTALLFLAG_FORCE + // | INSTALLFLAG_NONINTERACTIVE // INSTALLFLAG_NONINTERACTIVE may cause error 0xe0000247 + , + rebootRequired + )) + { + DWORD error = GetLastError(); + if (error != 0) + { + SetLastMsg("UpdateDriverForPlugAndPlayDevices failed, last error 0x%x\n", error); + if (g_printMsg) + { + printf(g_lastMsg); + } + return FALSE; + } + } + + return TRUE; +} + +BOOL Uninstall(LPCTSTR fullInfPath, PBOOL rebootRequired) +{ + SetLastMsg("Sucess"); + + if (FALSE == DiUninstallDriver( + NULL, + fullInfPath, + 0, + rebootRequired + )) + { + DWORD error = GetLastError(); + if (error != 0) + { + SetLastMsg("DiUninstallDriver failed, last error 0x%x\n", error); + if (g_printMsg) + { + printf(g_lastMsg); + } + return FALSE; + } + } + + return TRUE; +} + +BOOL IsDeviceCreated(PBOOL created) +{ + SetLastMsg("Sucess"); + + HDEVINFO hardwareDeviceInfo = SetupDiGetClassDevs( + &GUID_DEVINTERFACE_IDD_DRIVER_DEVICE, + NULL, // Define no enumerator (global) + NULL, // Define no + (DIGCF_PRESENT | // Only Devices present + DIGCF_DEVICEINTERFACE)); // Function class devices. + if (INVALID_HANDLE_VALUE == hardwareDeviceInfo) + { + SetLastMsg("Idd device: SetupDiGetClassDevs failed, last error 0x%x\n", GetLastError()); + if (g_printMsg) + { + printf(g_lastMsg); + } + return FALSE; + } + + SP_DEVICE_INTERFACE_DATA deviceInterfaceData; + deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + BOOL ret = FALSE; + do + { + if (TRUE == SetupDiEnumDeviceInterfaces(hardwareDeviceInfo, + 0, // No care about specific PDOs + &GUID_DEVINTERFACE_IDD_DRIVER_DEVICE, + 0, // + &deviceInterfaceData)) + { + *created = TRUE; + ret = TRUE; + break; + } + + DWORD error = GetLastError(); + if (error == ERROR_NO_MORE_ITEMS) + { + *created = FALSE; + ret = TRUE; + break; + } + + SetLastMsg("Idd device: SetupDiEnumDeviceInterfaces failed, last error 0x%x\n", error); + if (g_printMsg) + { + printf(g_lastMsg); + } + ret = FALSE; + break; + + } while (0); + + (VOID)SetupDiDestroyDeviceInfoList(hardwareDeviceInfo); + return ret; +} + +BOOL DeviceCreate(PHSWDEVICE hSwDevice) +{ + SetLastMsg("Sucess"); + + if (*hSwDevice != NULL) + { + SetLastMsg("Device handler is not NULL\n"); + return FALSE; + } + + BOOL created = TRUE; + if (FALSE == IsDeviceCreated(&created)) + { + return FALSE; + } + if (created == TRUE) + { + SetLastMsg("Device is created before, please uninstall it first\n"); + if (g_printMsg) + { + printf(g_lastMsg); + } + return FALSE; + } + + // create device + HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if (hEvent == INVALID_HANDLE_VALUE || hEvent == NULL) + { + DWORD error = GetLastError(); + SetLastMsg("CreateEvent failed 0x%lx\n", error); + if (g_printMsg) + { + printf(g_lastMsg); + } + + return FALSE; + } + + SW_DEVICE_CREATE_INFO createInfo = { 0 }; + PCWSTR description = L"RustDesk Idd Driver"; + + // These match the Pnp id's in the inf file so OS will load the driver when the device is created + PCWSTR instanceId = L"RustDeskIddDriver"; + PCWSTR hardwareIds = L"RustDeskIddDriver\0\0"; + PCWSTR compatibleIds = L"RustDeskIddDriver\0\0"; + + createInfo.cbSize = sizeof(createInfo); + createInfo.pszzCompatibleIds = compatibleIds; + createInfo.pszInstanceId = instanceId; + createInfo.pszzHardwareIds = hardwareIds; + createInfo.pszDeviceDescription = description; + + createInfo.CapabilityFlags = SWDeviceCapabilitiesRemovable | + SWDeviceCapabilitiesSilentInstall | + SWDeviceCapabilitiesDriverRequired; + + // Create the device + HRESULT hr = SwDeviceCreate(L"RustDeskIddDriver", + L"HTREE\\ROOT\\0", + &createInfo, + 0, + NULL, + CreationCallback, + &hEvent, + hSwDevice); + if (FAILED(hr)) + { + SetLastMsg("SwDeviceCreate failed with 0x%lx\n", hr); + if (g_printMsg) + { + printf(g_lastMsg); + } + + return FALSE; + } + + // Wait for callback to signal that the device has been created + printf("Waiting for device to be created....\n"); + DWORD waitResult = WaitForSingleObject(hEvent, 10 * 1000); + if (waitResult != WAIT_OBJECT_0) + { + SetLastMsg("Wait for device creation failed 0x%d\n", waitResult); + if (g_printMsg) + { + printf(g_lastMsg); + } + return FALSE; + } + // printf("Device created\n\n"); + return TRUE; +} + +VOID DeviceClose(HSWDEVICE hSwDevice) +{ + SetLastMsg("Sucess"); + + if (hSwDevice != INVALID_HANDLE_VALUE && hSwDevice != NULL) + { + SwDeviceClose(hSwDevice); + } +} + +BOOL MonitorPlugIn(UINT index, UINT edid, INT retries) +{ + SetLastMsg("Sucess"); + + if (retries < 0) + { + SetLastMsg("invalid tries %d\n", retries); + if (g_printMsg) + { + printf(g_lastMsg); + } + return FALSE; + } + + HANDLE hDevice = INVALID_HANDLE_VALUE; + for (; retries >= 0; --retries) + { + hDevice = DeviceOpenHandle(); + if (hDevice != INVALID_HANDLE_VALUE && hDevice != NULL) + { + break; + } + Sleep(1000); + } + if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL) + { + return FALSE; + } + + BOOL ret = FALSE; + DWORD junk = 0; + CtlPlugIn plugIn; + plugIn.ConnectorIndex = index; + plugIn.MonitorEDID = edid; + HRESULT hr = CoCreateGuid(&plugIn.ContainerId); + if (!SUCCEEDED(hr)) + { + SetLastMsg("CoCreateGuid failed %d\n", hr); + if (g_printMsg) + { + printf(g_lastMsg); + } + ret = FALSE; + } + else + { + ret = FALSE; + for (; retries >= 0; --retries) + { + if (TRUE == DeviceIoControl( + hDevice, + IOCTL_CHANGER_IDD_PLUG_IN, + &plugIn, // Ptr to InBuffer + sizeof(CtlPlugIn), // Length of InBuffer + NULL, // Ptr to OutBuffer + 0, // Length of OutBuffer + &junk, // BytesReturned + 0)) // Ptr to Overlapped structure + { + ret = TRUE; + break; + } + } + if (ret == FALSE) + { + DWORD error = GetLastError(); + SetLastMsg("DeviceIoControl failed 0x%lx\n", error); + printf(g_lastMsg); + } + } + + DeviceCloseHandle(hDevice); + return ret; +} + +BOOL MonitorPlugOut(UINT index) +{ + SetLastMsg("Sucess"); + + HANDLE hDevice = DeviceOpenHandle(); + if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL) + { + return FALSE; + } + + BOOL ret = FALSE; + DWORD junk = 0; + CtlPlugOut plugOut; + plugOut.ConnectorIndex = index; + if (!DeviceIoControl( + hDevice, + IOCTL_CHANGER_IDD_PLUG_OUT, + &plugOut, // Ptr to InBuffer + sizeof(CtlPlugOut), // Length of InBuffer + NULL, // Ptr to OutBuffer + 0, // Length of OutBuffer + &junk, // BytesReturned + 0)) // Ptr to Overlapped structure + { + DWORD error = GetLastError(); + SetLastMsg("DeviceIoControl failed 0x%lx\n", error); + if (g_printMsg) + { + printf(g_lastMsg); + } + ret = FALSE; + } + else + { + ret = TRUE; + } + + DeviceCloseHandle(hDevice); + return ret; +} + +BOOL MonitorModesUpdate(UINT index, UINT modeCount, PMonitorMode modes) +{ + SetLastMsg("Sucess"); + + HANDLE hDevice = DeviceOpenHandle(); + if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL) + { + return FALSE; + } + + BOOL ret = FALSE; + DWORD junk = 0; + size_t buflen = sizeof(UINT) * 2 + modeCount * sizeof(MonitorMode); + PCtlMonitorModes pMonitorModes = (PCtlMonitorModes)malloc(buflen); + if (pMonitorModes == NULL) + { + SetLastMsg("CtlMonitorModes malloc failed 0x%lx\n"); + if (g_printMsg) + { + printf(g_lastMsg); + } + return FALSE; + } + + pMonitorModes->ConnectorIndex = index; + pMonitorModes->ModeCount = modeCount; + for (UINT i = 0; i < modeCount; ++i) + { + pMonitorModes->Modes[i].Width = modes[i].width; + pMonitorModes->Modes[i].Height = modes[i].height; + pMonitorModes->Modes[i].Sync = modes[i].sync; + } + if (!DeviceIoControl( + hDevice, + IOCTL_CHANGER_IDD_UPDATE_MONITOR_MODE, + pMonitorModes, // Ptr to InBuffer + buflen, // Length of InBuffer + NULL, // Ptr to OutBuffer + 0, // Length of OutBuffer + &junk, // BytesReturned + 0)) // Ptr to Overlapped structure + { + DWORD error = GetLastError(); + SetLastMsg("DeviceIoControl failed 0x%lx\n", error); + if (g_printMsg) + { + printf(g_lastMsg); + } + ret = FALSE; + } + else + { + ret = TRUE; + } + + free(pMonitorModes); + DeviceCloseHandle(hDevice); + return ret; +} + +VOID WINAPI +CreationCallback( + _In_ HSWDEVICE hSwDevice, + _In_ HRESULT hrCreateResult, + _In_opt_ PVOID pContext, + _In_opt_ PCWSTR pszDeviceInstanceId +) +{ + HANDLE hEvent = *(HANDLE*)pContext; + + SetEvent(hEvent); + UNREFERENCED_PARAMETER(hSwDevice); + UNREFERENCED_PARAMETER(hrCreateResult); + // printf("Idd device %ls created\n", pszDeviceInstanceId); +} + +BOOLEAN +GetDevicePath( + _In_ LPCGUID InterfaceGuid, + _Out_writes_(BufLen) PTCHAR DevicePath, + _In_ size_t BufLen +) +{ + CONFIGRET cr = CR_SUCCESS; + PTSTR deviceInterfaceList = NULL; + ULONG deviceInterfaceListLength = 0; + PTSTR nextInterface; + HRESULT hr = E_FAIL; + BOOLEAN bRet = TRUE; + + cr = CM_Get_Device_Interface_List_Size( + &deviceInterfaceListLength, + (LPGUID)InterfaceGuid, + NULL, + CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES); + if (cr != CR_SUCCESS) + { + SetLastMsg("Error GetDevicePath 0x%x retrieving device interface list size.\n", cr); + if (g_printMsg) + { + printf(g_lastMsg); + } + + goto clean0; + } + + // CAUTION: BUG here. deviceInterfaceListLength is greater than 1, even device was not created... + if (deviceInterfaceListLength <= 1) + { + SetLastMsg("Error: GetDevicePath No active device interfaces found. Is the sample driver loaded?\n"); + if (g_printMsg) + { + printf(g_lastMsg); + } + bRet = FALSE; + goto clean0; + } + + deviceInterfaceList = (PTSTR)malloc(deviceInterfaceListLength * sizeof(TCHAR)); + if (deviceInterfaceList == NULL) + { + SetLastMsg("Error GetDevicePath allocating memory for device interface list.\n"); + if (g_printMsg) + { + printf(g_lastMsg); + } + bRet = FALSE; + goto clean0; + } + ZeroMemory(deviceInterfaceList, deviceInterfaceListLength * sizeof(TCHAR)); + + for (int i = 0; i < 3 && _tcslen(deviceInterfaceList) == 0; i++) + { + // CAUTION: BUG here. deviceInterfaceList is NULL, even device was not created... + cr = CM_Get_Device_Interface_List( + (LPGUID)InterfaceGuid, + NULL, + deviceInterfaceList, + deviceInterfaceListLength, + CM_GET_DEVICE_INTERFACE_LIST_PRESENT); + if (cr != CR_SUCCESS) + { + SetLastMsg("Error GetDevicePath 0x%x retrieving device interface list.\n", cr); + if (g_printMsg) + { + printf(g_lastMsg); + } + goto clean0; + } + _tprintf(_T("get deviceInterfaceList %s\n"), deviceInterfaceList); + Sleep(1000); + } + + nextInterface = deviceInterfaceList + _tcslen(deviceInterfaceList) + 1; +#ifdef UNICODE + if (*nextInterface != UNICODE_NULL) { +#else + if (*nextInterface != ANSI_NULL) { +#endif + printf("Warning: More than one device interface instance found. \n" + "Selecting first matching device.\n\n"); + } + + printf("begin copy device path\n"); + hr = StringCchCopy(DevicePath, BufLen, deviceInterfaceList); + if (FAILED(hr)) + { + SetLastMsg("Error: GetDevicePath StringCchCopy failed with HRESULT 0x%x", hr); + if (g_printMsg) + { + printf(g_lastMsg); + } + bRet = FALSE; + goto clean0; + } + +clean0: + if (deviceInterfaceList != NULL) + { + free(deviceInterfaceList); + } + if (CR_SUCCESS != cr) + { + bRet = FALSE; + } + + return bRet; +} + +BOOLEAN GetDevicePath2( + _In_ LPCGUID InterfaceGuid, + _Out_writes_(BufLen) PTCHAR DevicePath, + _In_ size_t BufLen +) +{ + HANDLE hDevice = INVALID_HANDLE_VALUE; + PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = NULL; + ULONG predictedLength = 0; + ULONG requiredLength = 0; + ULONG bytes; + HDEVINFO hardwareDeviceInfo; + SP_DEVICE_INTERFACE_DATA deviceInterfaceData; + BOOLEAN status = FALSE; + HRESULT hr; + + hardwareDeviceInfo = SetupDiGetClassDevs( + InterfaceGuid, + NULL, // Define no enumerator (global) + NULL, // Define no + (DIGCF_PRESENT | // Only Devices present + DIGCF_DEVICEINTERFACE)); // Function class devices. + if (INVALID_HANDLE_VALUE == hardwareDeviceInfo) + { + SetLastMsg("Idd device: SetupDiGetClassDevs failed, last error 0x%x\n", GetLastError()); + if (g_printMsg) + { + printf(g_lastMsg); + } + return FALSE; + } + + deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + if (!SetupDiEnumDeviceInterfaces(hardwareDeviceInfo, + 0, // No care about specific PDOs + InterfaceGuid, + 0, // + &deviceInterfaceData)) + { + SetLastMsg("Idd device: SetupDiEnumDeviceInterfaces failed, last error 0x%x\n", GetLastError()); + if (g_printMsg) + { + printf(g_lastMsg); + } + goto Clean0; + } + + // + // Allocate a function class device data structure to receive the + // information about this particular device. + // + SetupDiGetDeviceInterfaceDetail( + hardwareDeviceInfo, + &deviceInterfaceData, + NULL, // probing so no output buffer yet + 0, // probing so output buffer length of zero + &requiredLength, + NULL);//not interested in the specific dev-node + + if (ERROR_INSUFFICIENT_BUFFER != GetLastError()) + { + SetLastMsg("Idd device: SetupDiGetDeviceInterfaceDetail failed, last error 0x%x\n", GetLastError()); + if (g_printMsg) + { + printf(g_lastMsg); + } + goto Clean0; + } + + predictedLength = requiredLength; + deviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)HeapAlloc( + GetProcessHeap(), + HEAP_ZERO_MEMORY, + predictedLength + ); + + if (deviceInterfaceDetailData) + { + deviceInterfaceDetailData->cbSize = + sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + } + else + { + SetLastMsg("Idd device: HeapAlloc failed, last error 0x%x\n", GetLastError()); + if (g_printMsg) + { + printf(g_lastMsg); + } + goto Clean0; + } + + if (!SetupDiGetDeviceInterfaceDetail( + hardwareDeviceInfo, + &deviceInterfaceData, + deviceInterfaceDetailData, + predictedLength, + &requiredLength, + NULL)) + { + SetLastMsg("Idd device: SetupDiGetDeviceInterfaceDetail failed, last error 0x%x\n", GetLastError()); + if (g_printMsg) + { + printf(g_lastMsg); + } + goto Clean1; + } + + hr = StringCchCopy(DevicePath, BufLen, deviceInterfaceDetailData->DevicePath); + if (FAILED(hr)) + { + SetLastMsg("Error: StringCchCopy failed with HRESULT 0x%x", hr); + if (g_printMsg) + { + printf(g_lastMsg); + } + status = FALSE; + goto Clean1; + } + else + { + status = TRUE; + } + +Clean1: + (VOID)HeapFree(GetProcessHeap(), 0, deviceInterfaceDetailData); +Clean0: + (VOID)SetupDiDestroyDeviceInfoList(hardwareDeviceInfo); + return status; +} + +// https://stackoverflow.com/questions/67164846/createfile-fails-unless-i-disable-enable-my-device +HANDLE DeviceOpenHandle() +{ + SetLastMsg("Sucess"); + + // const int maxDevPathLen = 256; + TCHAR devicePath[256] = { 0 }; + HANDLE hDevice = INVALID_HANDLE_VALUE; + do + { + if (FALSE == GetDevicePath2( + &GUID_DEVINTERFACE_IDD_DRIVER_DEVICE, + devicePath, + sizeof(devicePath) / sizeof(devicePath[0]))) + { + break; + } + if (_tcslen(devicePath) == 0) + { + SetLastMsg("GetDevicePath got empty device path\n"); + if (g_printMsg) + { + printf(g_lastMsg); + } + break; + } + + _tprintf(_T("Idd device: try open %s\n"), devicePath); + hDevice = CreateFile( + devicePath, + GENERIC_READ | GENERIC_WRITE, + // FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + NULL, // no SECURITY_ATTRIBUTES structure + OPEN_EXISTING, // No special create flags + 0, // No special attributes + NULL + ); + if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL) + { + DWORD error = GetLastError(); + SetLastMsg("CreateFile failed 0x%lx\n", error); + if (g_printMsg) + { + printf(g_lastMsg); + } + } + } while (0); + + return hDevice; +} + +VOID DeviceCloseHandle(HANDLE handle) +{ + if (handle != INVALID_HANDLE_VALUE && handle != NULL) + { + CloseHandle(handle); + } +} + +VOID SetPrintErrMsg(BOOL b) +{ + g_printMsg = (b == TRUE); +} diff --git a/libs/virtual_display/src/win10/IddController.h b/libs/virtual_display/src/win10/IddController.h new file mode 100644 index 000000000..f92f72647 --- /dev/null +++ b/libs/virtual_display/src/win10/IddController.h @@ -0,0 +1,144 @@ +#pragma once +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Install or Update RustDeskIddDriver. + * + * @param fullInfPath [in] Full path of the driver inf file. + * @param rebootRequired [out] Indicates whether a restart is required. + * + * @return TRUE/FALSE. If FALSE returned, error message can be retrieved by GetLastMsg() + * + * @see GetLastMsg#GetLastMsg + */ +BOOL InstallUpdate(LPCTSTR fullInfPath, PBOOL rebootRequired); + +/** + * @brief Uninstall RustDeskIddDriver. + * + * @param fullInfPath [in] Full path of the driver inf file. + * @param rebootRequired [out] Indicates whether a restart is required. + * + * @return TRUE/FALSE. If FALSE returned, error message can be retrieved by GetLastMsg() + * + * @see GetLastMsg#GetLastMsg + */ +BOOL Uninstall(LPCTSTR fullInfPath, PBOOL rebootRequired); + +/** + * @brief Check if RustDeskIddDriver device is created before. + * The driver device(adapter) should be single instance. + * + * @param created [out] Indicates whether the device is created before. + * + * @return TRUE/FALSE. If FALSE returned, error message can be retrieved by GetLastMsg() + * + * @see GetLastMsg#GetLastMsg + * + */ +BOOL IsDeviceCreated(PBOOL created); + +/** + * @brief Create device. + * Only one device should be created. + * If device is installed ealier, this function returns FALSE. + * + * @param hSwDevice [out] Handler of software device, used by DeviceCreate(). Should be **NULL**. + * + * @return TRUE/FALSE. If FALSE returned, error message can be retrieved by GetLastMsg() + * + * @see GetLastMsg#GetLastMsg + * + */ +BOOL DeviceCreate(PHSWDEVICE hSwDevice); + +/** + * @brief Close device. + * + * @param hSwDevice Handler of software device, used by SwDeviceClose(). + * + */ +VOID DeviceClose(HSWDEVICE hSwDevice); + +/** + * @brief Plug in monitor. + * + * @param index [in] Monitor index, should be 0, 1, 2. + * @param edid [in] Monitor edid. + * 0 Modified EDID from Dell S2719DGF + * 1 Modified EDID from Lenovo Y27fA + * @param retries [in] Retry times. Retry 1 time / sec. 25~30 seconds may be good choices. + * -1 is invalid. + * 0 means doing once and no retries. + * 1 means doing once and retry one time... + * + * @return TRUE/FALSE. If FALSE returned, error message can be retrieved by GetLastMsg() + * + * @see GetLastMsg#GetLastMsg + * + * @remark Plug in monitor may fail if device is created in a very short time. + * System need some time to prepare the device. + * + */ +BOOL MonitorPlugIn(UINT index, UINT edid, INT retries); + +/** + * @brief Plug out monitor. + * + * @param index [in] Monitor index, should be 0, 1, 2. + * + * @return TRUE/FALSE. If FALSE returned, error message can be retrieved by GetLastMsg() + * + * @see GetLastMsg#GetLastMsg + * + */ +BOOL MonitorPlugOut(UINT index); + +typedef struct _MonitorMode { + DWORD width; + DWORD height; + // Sync affects frequency. + DWORD sync; +} MonitorMode, *PMonitorMode; + +/** + * @brief Update monitor mode. + * + * @param index [in] Monitor index, should be 0, 1, 2. + * @param modeCount [in] Monitor mode count. + * @param MonitorMode [in] Monitor mode data. + * + * @return TRUE/FALSE. If FALSE returned, error message can be retrieved by GetLastMsg() + * + * @see GetLastMsg#GetLastMsg + * + */ +BOOL MonitorModesUpdate(UINT index, UINT modeCount, PMonitorMode modes); + +/** + * @brief Get last error message. + * + * @return Message string. The string is at most 1024 bytes. + * + */ +const char* GetLastMsg(); + +/** + * @brief Set if print error message when debug. + * + * @param b [in] TRUE to enable printing message. + * + * @remark For now, no need to read evironment variable to check if should print. + * + */ +VOID SetPrintErrMsg(BOOL b); + +#ifdef __cplusplus +} +#endif diff --git a/libs/virtual_display/src/win10/Public.h b/libs/virtual_display/src/win10/Public.h new file mode 100644 index 000000000..d7f294a52 --- /dev/null +++ b/libs/virtual_display/src/win10/Public.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include + +#define IOCTL_CHANGER_IDD_PLUG_IN CTL_CODE(IOCTL_CHANGER_BASE, \ + 0x1001, \ + METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) +#define IOCTL_CHANGER_IDD_PLUG_OUT CTL_CODE(IOCTL_CHANGER_BASE, \ + 0x1002, \ + METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) +#define IOCTL_CHANGER_IDD_UPDATE_MONITOR_MODE CTL_CODE(IOCTL_CHANGER_BASE, \ + 0x1003, \ + METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) + + +#define STATUS_ERROR_ADAPTER_NOT_INIT (3 << 30) + 11 +//#define STATUS_ERROR_IO_CTL_GET_INPUT (3 << 30) + 21 +//#define STATUS_ERROR_IO_CTL_GET_OUTPUT (3 << 30) + 22 +#define STATUS_ERROR_MONITOR_EXISTS (3 << 30) + 51 +#define STATUS_ERROR_MONITOR_NOT_EXISTS (3 << 30) + 52 +#define STATUS_ERROR_MONITOR_INVALID_PARAM (3 << 30) + 53 +#define STATUS_ERROR_MONITOR_OOM (3 << 30) + 54 + +#define MONITOR_EDID_MOD_DELL_S2719DGF 0 +#define MONITOR_EDID_MOD_LENOVO_Y27fA 1 + +typedef struct _CtlPlugIn { + UINT ConnectorIndex; + UINT MonitorEDID; + GUID ContainerId; +} CtlPlugIn, *PCtlPlugIn; + +typedef struct _CtlPlugOut { + UINT ConnectorIndex; +} CtlPlugOut, *PCtlPlugOut; + +typedef struct _CtlMonitorModes { + UINT ConnectorIndex; + UINT ModeCount; + struct { + DWORD Width; + DWORD Height; + DWORD Sync; + } Modes[1]; +} CtlMonitorModes, *PCtlMonitorModes; + + +#define SYMBOLIC_LINK_NAME L"\\Device\\RustDeskIddDriver" + diff --git a/libs/virtual_display/src/win10/idd.rs b/libs/virtual_display/src/win10/idd.rs new file mode 100644 index 000000000..ff614c058 --- /dev/null +++ b/libs/virtual_display/src/win10/idd.rs @@ -0,0 +1,215 @@ +#![allow(dead_code)] +#![allow(non_camel_case_types)] +#![allow(unused_variables)] +#![allow(non_snake_case)] +#![allow(deref_nullptr)] + +pub type size_t = ::std::os::raw::c_ulonglong; +pub type __vcrt_bool = bool; +pub type wchar_t = ::std::os::raw::c_ushort; + +pub type POINTER_64_INT = ::std::os::raw::c_ulonglong; +pub type INT8 = ::std::os::raw::c_schar; +pub type PINT8 = *mut ::std::os::raw::c_schar; +pub type INT16 = ::std::os::raw::c_short; +pub type PINT16 = *mut ::std::os::raw::c_short; +pub type INT32 = ::std::os::raw::c_int; +pub type PINT32 = *mut ::std::os::raw::c_int; +pub type INT64 = ::std::os::raw::c_longlong; +pub type PINT64 = *mut ::std::os::raw::c_longlong; +pub type UINT8 = ::std::os::raw::c_uchar; +pub type PUINT8 = *mut ::std::os::raw::c_uchar; +pub type UINT16 = ::std::os::raw::c_ushort; +pub type PUINT16 = *mut ::std::os::raw::c_ushort; +pub type UINT32 = ::std::os::raw::c_uint; +pub type PUINT32 = *mut ::std::os::raw::c_uint; +pub type UINT64 = ::std::os::raw::c_ulonglong; +pub type PUINT64 = *mut ::std::os::raw::c_ulonglong; +pub type LONG32 = ::std::os::raw::c_int; +pub type PLONG32 = *mut ::std::os::raw::c_int; +pub type ULONG32 = ::std::os::raw::c_uint; +pub type PULONG32 = *mut ::std::os::raw::c_uint; +pub type DWORD32 = ::std::os::raw::c_uint; +pub type PDWORD32 = *mut ::std::os::raw::c_uint; +pub type INT_PTR = ::std::os::raw::c_longlong; +pub type PINT_PTR = *mut ::std::os::raw::c_longlong; +pub type UINT_PTR = ::std::os::raw::c_ulonglong; +pub type PUINT_PTR = *mut ::std::os::raw::c_ulonglong; +pub type LONG_PTR = ::std::os::raw::c_longlong; +pub type PLONG_PTR = *mut ::std::os::raw::c_longlong; +pub type ULONG_PTR = ::std::os::raw::c_ulonglong; +pub type PULONG_PTR = *mut ::std::os::raw::c_ulonglong; +pub type SHANDLE_PTR = ::std::os::raw::c_longlong; +pub type HANDLE_PTR = ::std::os::raw::c_ulonglong; +pub type UHALF_PTR = ::std::os::raw::c_uint; +pub type PUHALF_PTR = *mut ::std::os::raw::c_uint; +pub type HALF_PTR = ::std::os::raw::c_int; +pub type PHALF_PTR = *mut ::std::os::raw::c_int; +pub type SIZE_T = ULONG_PTR; +pub type PSIZE_T = *mut ULONG_PTR; +pub type SSIZE_T = LONG_PTR; +pub type PSSIZE_T = *mut LONG_PTR; +pub type DWORD_PTR = ULONG_PTR; +pub type PDWORD_PTR = *mut ULONG_PTR; +pub type LONG64 = ::std::os::raw::c_longlong; +pub type PLONG64 = *mut ::std::os::raw::c_longlong; +pub type ULONG64 = ::std::os::raw::c_ulonglong; +pub type PULONG64 = *mut ::std::os::raw::c_ulonglong; +pub type DWORD64 = ::std::os::raw::c_ulonglong; +pub type PDWORD64 = *mut ::std::os::raw::c_ulonglong; +pub type KAFFINITY = ULONG_PTR; +pub type PKAFFINITY = *mut KAFFINITY; +pub type PVOID = *mut ::std::os::raw::c_void; +pub type CHAR = ::std::os::raw::c_char; +pub type SHORT = ::std::os::raw::c_short; +pub type LONG = ::std::os::raw::c_long; +pub type WCHAR = wchar_t; +pub type PWCHAR = *mut WCHAR; +pub type LPWCH = *mut WCHAR; +pub type PWCH = *mut WCHAR; +pub type LPCWCH = *const WCHAR; +pub type PCWCH = *const WCHAR; +pub type NWPSTR = *mut WCHAR; +pub type LPWSTR = *mut WCHAR; +pub type PWSTR = *mut WCHAR; +pub type PZPWSTR = *mut PWSTR; +pub type PCZPWSTR = *const PWSTR; +pub type LPUWSTR = *mut WCHAR; +pub type PUWSTR = *mut WCHAR; +pub type LPCWSTR = *const WCHAR; +pub type PCWSTR = *const WCHAR; +pub type PZPCWSTR = *mut PCWSTR; +pub type PCZPCWSTR = *const PCWSTR; +pub type LPCUWSTR = *const WCHAR; +pub type PCUWSTR = *const WCHAR; +pub type PZZWSTR = *mut WCHAR; +pub type PCZZWSTR = *const WCHAR; +pub type PUZZWSTR = *mut WCHAR; +pub type PCUZZWSTR = *const WCHAR; +pub type PNZWCH = *mut WCHAR; +pub type PCNZWCH = *const WCHAR; +pub type PUNZWCH = *mut WCHAR; +pub type PCUNZWCH = *const WCHAR; +pub type PCHAR = *mut CHAR; +pub type LPCH = *mut CHAR; +pub type PCH = *mut CHAR; +pub type LPCCH = *const CHAR; +pub type PCCH = *const CHAR; +pub type NPSTR = *mut CHAR; +pub type LPSTR = *mut CHAR; +pub type PSTR = *mut CHAR; +pub type PZPSTR = *mut PSTR; +pub type PCZPSTR = *const PSTR; +pub type LPCSTR = *const CHAR; +pub type PCSTR = *const CHAR; +pub type PZPCSTR = *mut PCSTR; +pub type PCZPCSTR = *const PCSTR; +pub type PZZSTR = *mut CHAR; +pub type PCZZSTR = *const CHAR; +pub type PNZCH = *mut CHAR; +pub type PCNZCH = *const CHAR; +pub type TCHAR = ::std::os::raw::c_char; +pub type PTCHAR = *mut ::std::os::raw::c_char; +pub type TBYTE = ::std::os::raw::c_uchar; +pub type PTBYTE = *mut ::std::os::raw::c_uchar; +pub type LPTCH = LPCH; +pub type PTCH = LPCH; +pub type LPCTCH = LPCCH; +pub type PCTCH = LPCCH; +pub type PTSTR = LPSTR; +pub type LPTSTR = LPSTR; +pub type PUTSTR = LPSTR; +pub type LPUTSTR = LPSTR; +pub type PCTSTR = LPCSTR; +pub type LPCTSTR = LPCSTR; +pub type PCUTSTR = LPCSTR; +pub type LPCUTSTR = LPCSTR; +pub type PZZTSTR = PZZSTR; +pub type PUZZTSTR = PZZSTR; +pub type PCZZTSTR = PCZZSTR; +pub type PCUZZTSTR = PCZZSTR; +pub type PZPTSTR = PZPSTR; +pub type PNZTCH = PNZCH; +pub type PUNZTCH = PNZCH; +pub type PCNZTCH = PCNZCH; +pub type PCUNZTCH = PCNZCH; +pub type PSHORT = *mut SHORT; +pub type PLONG = *mut LONG; +pub type ULONG = ::std::os::raw::c_ulong; +pub type PULONG = *mut ULONG; +pub type USHORT = ::std::os::raw::c_ushort; +pub type PUSHORT = *mut USHORT; +pub type UCHAR = ::std::os::raw::c_uchar; +pub type PUCHAR = *mut UCHAR; +pub type PSZ = *mut ::std::os::raw::c_char; +pub type DWORD = ::std::os::raw::c_ulong; +pub type BOOL = ::std::os::raw::c_int; +pub type BYTE = ::std::os::raw::c_uchar; +pub type WORD = ::std::os::raw::c_ushort; +pub type FLOAT = f32; +pub type PFLOAT = *mut FLOAT; +pub type PBOOL = *mut BOOL; +pub type LPBOOL = *mut BOOL; +pub type PBYTE = *mut BYTE; +pub type LPBYTE = *mut BYTE; +pub type PINT = *mut ::std::os::raw::c_int; +pub type LPINT = *mut ::std::os::raw::c_int; +pub type PWORD = *mut WORD; +pub type LPWORD = *mut WORD; +pub type LPLONG = *mut ::std::os::raw::c_long; +pub type PDWORD = *mut DWORD; +pub type LPDWORD = *mut DWORD; +pub type LPVOID = *mut ::std::os::raw::c_void; +pub type LPCVOID = *const ::std::os::raw::c_void; +pub type INT = ::std::os::raw::c_int; +pub type UINT = ::std::os::raw::c_uint; +pub type PUINT = *mut ::std::os::raw::c_uint; +pub type va_list = *mut ::std::os::raw::c_char; + +pub const TRUE: ::std::os::raw::c_int = 1; +pub const FALSE: ::std::os::raw::c_int = 0; + +#[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; + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct HSWDEVICE__ { + pub unused: ::std::os::raw::c_int, +} +pub type HSWDEVICE = *mut HSWDEVICE__; +pub type PHSWDEVICE = *mut HSWDEVICE; + +#[link(name = "Newdev")] +extern "C" { + pub fn InstallUpdate(fullInfPath: LPCTSTR, rebootRequired: PBOOL) -> BOOL; +} + +#[link(name = "Setupapi")] +extern "C" { + pub fn IsDeviceCreated(created: PBOOL) -> BOOL; +} + +#[link(name = "Swdevice")] +#[link(name = "OneCoreUAP")] +extern "C" { + pub fn DeviceCreate(hSwDevice: PHSWDEVICE) -> BOOL; + pub fn DeviceClose(hSwDevice: HSWDEVICE); +} + +extern "C" { + pub fn Uninstall(fullInfPath: LPCTSTR, rebootRequired: PBOOL) -> BOOL; + pub fn MonitorPlugIn(index: UINT, edid: UINT, retries: INT) -> BOOL; + pub fn MonitorPlugOut(index: UINT) -> BOOL; + pub fn MonitorModesUpdate(index: UINT, modeCount: UINT, modes: PMonitorMode) -> BOOL; + + pub fn GetLastMsg() -> PCHAR; + pub fn SetPrintErrMsg(b: BOOL); +} diff --git a/libs/virtual_display/src/win10/mod.rs b/libs/virtual_display/src/win10/mod.rs new file mode 100644 index 000000000..7b787cafb --- /dev/null +++ b/libs/virtual_display/src/win10/mod.rs @@ -0,0 +1,9 @@ +pub mod idd; +use std::ffi::CStr; + +pub const DRIVER_INSTALL_PATH: &str = "RustDeskIddDriver/RustDeskIddDriver.inf"; +pub const DRIVER_DOWNLOAD_URL: &str = ""; + +pub unsafe fn get_last_msg() -> Result<&'static str, std::str::Utf8Error> { + CStr::from_ptr(idd::GetLastMsg()).to_str() +} diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 68faf178e..dd7f78d37 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -32,6 +32,7 @@ use std::{ io::ErrorKind::WouldBlock, time::{self, Duration, Instant}, }; +use virtual_display; const WAIT_BASE: i32 = 17; pub const NAME: &'static str = "video"; @@ -103,7 +104,7 @@ impl VideoFrameController { } Ok(Some((id, instant))) => { if let Some(tm) = instant { - log::trace!("channel recv latency: {}", tm.elapsed().as_secs_f32()); + log::trace!("Channel recv latency: {}", tm.elapsed().as_secs_f32()); } fetched_conn_ids.insert(id); @@ -133,7 +134,7 @@ fn check_display_changed( last_width: usize, last_hegiht: usize, ) -> bool { - let displays = match Display::all() { + let displays = match try_get_displays() { Ok(d) => d, _ => return false, }; @@ -158,6 +159,18 @@ fn check_display_changed( } fn run(sp: GenericService) -> 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(); + } + } + let fps = 30; let spf = time::Duration::from_secs_f32(1. / (fps as f32)); let (ndisplay, current, display) = get_current_display()?; @@ -298,6 +311,7 @@ fn run(sp: GenericService) -> ResultType<()> { std::thread::sleep(spf - elapsed); } } + Ok(()) } @@ -379,7 +393,7 @@ fn handle_one_frame( } fn get_display_num() -> usize { - if let Ok(d) = Display::all() { + if let Ok(d) = try_get_displays() { d.len() } else { 0 @@ -393,7 +407,7 @@ pub fn get_displays() -> ResultType<(usize, Vec)> { } let mut displays = Vec::new(); let mut primary = 0; - for (i, d) in Display::all()?.iter().enumerate() { + for (i, d) in try_get_displays()?.iter().enumerate() { if d.is_primary() { primary = i; } @@ -428,7 +442,7 @@ pub fn refresh() { } fn get_primary() -> usize { - if let Ok(all) = Display::all() { + if let Ok(all) = try_get_displays() { for (i, d) in all.iter().enumerate() { if d.is_primary() { return i; @@ -442,12 +456,42 @@ pub fn switch_to_primary() { switch_display(get_primary() as _); } +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 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() + } + } + Ok(displays) +} + fn get_current_display() -> ResultType<(usize, usize, Display)> { let mut current = *CURRENT_DISPLAY.lock().unwrap() as usize; - let mut displays = Display::all()?; + let mut displays = try_get_displays()?; if displays.len() == 0 { bail!("No displays"); } + let n = displays.len(); if current >= n { current = 0; diff --git a/src/ui.rs b/src/ui.rs index c59f01299..cc6347b84 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -19,6 +19,7 @@ use std::{ process::Child, sync::{Arc, Mutex}, }; +use virtual_display; pub type Childs = Arc)>>; @@ -364,6 +365,22 @@ impl UI { } } + // TODO: ui prompt + fn install_virtual_display(&self) { + let mut reboot_required = false; + match virtual_display::install_update_driver(&mut reboot_required) { + Ok(_) => { + log::info!( + "Virtual Display: install virtual display done, reboot is {} required", + if reboot_required { "" } else { "not" } + ); + } + Err(e) => { + log::error!("Virtual Display: install virtual display failed {}", e); + } + } + } + fn install_path(&mut self) -> String { #[cfg(windows)] return crate::platform::windows::get_install_info().1; @@ -690,6 +707,7 @@ impl sciter::EventHandler for UI { fn get_sound_inputs(); fn set_options(Value); fn set_option(String, String); + fn install_virtual_display(); fn get_software_update_url(); fn get_new_version(); fn get_version(); diff --git a/src/ui/index.tis b/src/ui/index.tis index add5acd94..d43f12760 100644 --- a/src/ui/index.tis +++ b/src/ui/index.tis @@ -168,6 +168,7 @@ class MyIdMenu: Reactor.Component {
  • {translate('ID/Relay Server')}
  • {translate('IP Whitelisting')}
  • {translate('Socks5 Proxy')}
  • + {is_win ?
  • Install virtual display
  • : ""}
  • {svg_checkmark}{translate("Enable Service")}
  • @@ -268,6 +269,8 @@ class MyIdMenu: Reactor.Component { } handler.set_socks(proxy, username, password); }, 240); + } else if (me.id == "install-virtual-display") { + handler.install_virtual_display(); } else if (me.id == "stop-service") { handler.set_option("stop-service", service_stopped ? "" : "Y"); } else if (me.id == "about") {