From e6bf858ae1b82cc62c0c53d9ec3c74775ed63fcc Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 11 Mar 2022 19:51:57 +0800 Subject: [PATCH 1/7] virtual display: compile example ok, not debug yet Signed-off-by: fufesou --- Cargo.lock | 12 + Cargo.toml | 3 +- libs/virtual_display/Cargo.toml | 15 + libs/virtual_display/README.md | 32 + libs/virtual_display/build.rs | 35 + .../examples/idd_controller.rs | 137 +++ libs/virtual_display/src/lib.rs | 2 + .../virtual_display/src/win10/IddController.c | 783 ++++++++++++++++++ .../virtual_display/src/win10/IddController.h | 141 ++++ libs/virtual_display/src/win10/Public.h | 50 ++ libs/virtual_display/src/win10/idd.rs | 215 +++++ libs/virtual_display/src/win10/mod.rs | 1 + 12 files changed, 1425 insertions(+), 1 deletion(-) create mode 100644 libs/virtual_display/Cargo.toml create mode 100644 libs/virtual_display/README.md create mode 100644 libs/virtual_display/build.rs create mode 100644 libs/virtual_display/examples/idd_controller.rs create mode 100644 libs/virtual_display/src/lib.rs create mode 100644 libs/virtual_display/src/win10/IddController.c create mode 100644 libs/virtual_display/src/win10/IddController.h create mode 100644 libs/virtual_display/src/win10/Public.h create mode 100644 libs/virtual_display/src/win10/idd.rs create mode 100644 libs/virtual_display/src/win10/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 4f1d53cd0..0c2daa406 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,17 @@ 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", + "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/virtual_display/Cargo.toml b/libs/virtual_display/Cargo.toml new file mode 100644 index 000000000..19da85222 --- /dev/null +++ b/libs/virtual_display/Cargo.toml @@ -0,0 +1,15 @@ +[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" diff --git a/libs/virtual_display/README.md b/libs/virtual_display/README.md new file mode 100644 index 000000000..dbc4ac6bd --- /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 use [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 + + +### 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..a91b39181 --- /dev/null +++ b/libs/virtual_display/examples/idd_controller.rs @@ -0,0 +1,137 @@ +#[cfg(windows)] +use virtual_display::win10::idd; + +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) { + println!("Plug in monitor begin"); + if idd::FALSE == idd::MonitorPlugIn(index, 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 relative_path = "RustDeskIddDriver/RustDeskIddDriver.inf"; + let abs_path = Path::new(relative_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"); + break; + } + 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), + '2' => plug_in(1), + '3' => plug_in(2), + '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); + } + } +} diff --git a/libs/virtual_display/src/lib.rs b/libs/virtual_display/src/lib.rs new file mode 100644 index 000000000..425e873d5 --- /dev/null +++ b/libs/virtual_display/src/lib.rs @@ -0,0 +1,2 @@ +#[cfg(windows)] +pub mod win10; diff --git a/libs/virtual_display/src/win10/IddController.c b/libs/virtual_display/src/win10/IddController.c new file mode 100644 index 000000000..49f9beee0 --- /dev/null +++ b/libs/virtual_display/src/win10/IddController.c @@ -0,0 +1,783 @@ +#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, 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; + 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..90d6c4295 --- /dev/null +++ b/libs/virtual_display/src/win10/IddController.h @@ -0,0 +1,141 @@ +#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 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, 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..547cf4351 --- /dev/null +++ b/libs/virtual_display/src/win10/Public.h @@ -0,0 +1,50 @@ +#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 + +typedef struct _CtlPlugIn { + UINT ConnectorIndex; + 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..66a90b059 --- /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, 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..d8797d709 --- /dev/null +++ b/libs/virtual_display/src/win10/mod.rs @@ -0,0 +1 @@ +pub mod idd; \ No newline at end of file From b734e8aee9bc4a311b3dfcc36d32ab4784453d0b Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 12 Mar 2022 19:51:55 +0800 Subject: [PATCH 2/7] add virtual display Signed-off-by: fufesou --- Cargo.lock | 1 + libs/hbb_common/src/lib.rs | 3 +- libs/virtual_display/Cargo.toml | 1 + .../examples/idd_controller.rs | 17 +-- libs/virtual_display/src/lib.rs | 101 ++++++++++++++++++ .../virtual_display/src/win10/IddController.c | 3 +- .../virtual_display/src/win10/IddController.h | 5 +- libs/virtual_display/src/win10/Public.h | 4 + libs/virtual_display/src/win10/idd.rs | 2 +- libs/virtual_display/src/win10/mod.rs | 5 +- src/server/video_service.rs | 7 ++ src/ui.rs | 14 +++ src/ui/index.tis | 3 + 13 files changed, 153 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0c2daa406..33bc04259 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4186,6 +4186,7 @@ name = "virtual_display" version = "0.1.0" dependencies = [ "cc", + "hbb_common", "lazy_static", "serde 1.0.136", "serde_derive", 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 index 19da85222..0f7535d46 100644 --- a/libs/virtual_display/Cargo.toml +++ b/libs/virtual_display/Cargo.toml @@ -13,3 +13,4 @@ thiserror = "1.0.30" lazy_static = "1.4" serde = "1.0" serde_derive = "1.0" +hbb_common = { path = "../hbb_common" } \ No newline at end of file diff --git a/libs/virtual_display/examples/idd_controller.rs b/libs/virtual_display/examples/idd_controller.rs index a91b39181..0bcb79d66 100644 --- a/libs/virtual_display/examples/idd_controller.rs +++ b/libs/virtual_display/examples/idd_controller.rs @@ -1,5 +1,5 @@ #[cfg(windows)] -use virtual_display::win10::idd; +use virtual_display::win10::{idd, DRIVER_INSTALL_PATH}; use std::{ ffi::{CStr, CString}, @@ -24,9 +24,9 @@ fn prompt_input() -> u8 { .unwrap_or(0) } -unsafe fn plug_in(index: idd::UINT) { +unsafe fn plug_in(index: idd::UINT, edid: idd::UINT) { println!("Plug in monitor begin"); - if idd::FALSE == idd::MonitorPlugIn(index, 25) { + if idd::FALSE == idd::MonitorPlugIn(index, edid, 25) { println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap()); } else { println!("Plug in monitor done"); @@ -58,8 +58,7 @@ unsafe fn plug_out(index: idd::UINT) { } fn main() { - let relative_path = "RustDeskIddDriver/RustDeskIddDriver.inf"; - let abs_path = Path::new(relative_path).canonicalize().unwrap(); + let abs_path = Path::new(DRIVER_INSTALL_PATH).canonicalize().unwrap(); let full_inf_path = abs_path.to_str().unwrap(); unsafe { @@ -121,9 +120,9 @@ fn main() { h_sw_device = invalid_device; println!("Close device done"); } - '1' => plug_in(0), - '2' => plug_in(1), - '3' => plug_in(2), + '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), @@ -133,5 +132,7 @@ fn main() { if !full_inf_path.is_null() { let _ = CString::from_raw(full_inf_path); } + + idd::DeviceClose(h_sw_device); } } diff --git a/libs/virtual_display/src/lib.rs b/libs/virtual_display/src/lib.rs index 425e873d5..2a37fb586 100644 --- a/libs/virtual_display/src/lib.rs +++ b/libs/virtual_display/src/lib.rs @@ -1,2 +1,103 @@ #[cfg(windows)] pub mod win10; + +use hbb_common::{bail, lazy_static, ResultType}; +use std::{ + ffi::{CStr, CString}, + path::Path, + sync::Mutex, +}; + +lazy_static::lazy_static! { + #[cfg(windows)] + static ref H_SW_DEVICE: Mutex = Mutex::new(0); +} + +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() -> 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), + }; + + unsafe { + #[cfg(windows)] + { + let mut reboot_required = win10::idd::FALSE; + if win10::idd::InstallUpdate(full_install_path, &mut reboot_required) + == win10::idd::FALSE + { + bail!("{}", CStr::from_ptr(win10::idd::GetLastMsg()).to_str()?); + } + } + } + + Ok(()) +} + +pub fn is_device_created() -> bool { + #[cfg(windows)] + return *H_SW_DEVICE.lock().unwrap() != 0; + #[cfg(not(windows))] + return false; +} + +#[cfg(windows)] +pub fn create_device() -> ResultType<()> { + unsafe { + let mut h_sw_device = *H_SW_DEVICE.lock().unwrap() as win10::idd::HSWDEVICE; + if win10::idd::DeviceCreate(&mut h_sw_device) == win10::idd::FALSE { + bail!("{}", CStr::from_ptr(win10::idd::GetLastMsg()).to_str()?); + } else { + *H_SW_DEVICE.lock().unwrap() = h_sw_device as u64; + Ok(()) + } + } +} + +#[cfg(windows)] +pub fn close_device() { + unsafe { + win10::idd::DeviceClose(*H_SW_DEVICE.lock().unwrap() as win10::idd::HSWDEVICE); + } +} + +#[cfg(windows)] +pub fn plug_in_monitor() -> ResultType<()> { + unsafe { + if win10::idd::MonitorPlugIn(0, 0, 30) == win10::idd::FALSE { + bail!("{}", CStr::from_ptr(win10::idd::GetLastMsg()).to_str()?); + } + Ok(()) + } +} + +#[cfg(windows)] +pub fn plug_out_monitor() -> ResultType<()> { + unsafe { + if win10::idd::MonitorPlugOut(0) == win10::idd::FALSE { + bail!("{}", CStr::from_ptr(win10::idd::GetLastMsg()).to_str()?); + } + Ok(()) + } +} diff --git a/libs/virtual_display/src/win10/IddController.c b/libs/virtual_display/src/win10/IddController.c index 49f9beee0..dd18a622d 100644 --- a/libs/virtual_display/src/win10/IddController.c +++ b/libs/virtual_display/src/win10/IddController.c @@ -282,7 +282,7 @@ VOID DeviceClose(HSWDEVICE hSwDevice) } } -BOOL MonitorPlugIn(UINT index, INT retries) +BOOL MonitorPlugIn(UINT index, UINT edid, INT retries) { SetLastMsg("Sucess"); @@ -315,6 +315,7 @@ BOOL MonitorPlugIn(UINT index, INT retries) DWORD junk = 0; CtlPlugIn plugIn; plugIn.ConnectorIndex = index; + plugIn.MonitorEDID = edid; HRESULT hr = CoCreateGuid(&plugIn.ContainerId); if (!SUCCEEDED(hr)) { diff --git a/libs/virtual_display/src/win10/IddController.h b/libs/virtual_display/src/win10/IddController.h index 90d6c4295..f92f72647 100644 --- a/libs/virtual_display/src/win10/IddController.h +++ b/libs/virtual_display/src/win10/IddController.h @@ -70,6 +70,9 @@ 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. @@ -83,7 +86,7 @@ VOID DeviceClose(HSWDEVICE hSwDevice); * System need some time to prepare the device. * */ -BOOL MonitorPlugIn(UINT index, INT retries); +BOOL MonitorPlugIn(UINT index, UINT edid, INT retries); /** * @brief Plug out monitor. diff --git a/libs/virtual_display/src/win10/Public.h b/libs/virtual_display/src/win10/Public.h index 547cf4351..d7f294a52 100644 --- a/libs/virtual_display/src/win10/Public.h +++ b/libs/virtual_display/src/win10/Public.h @@ -26,8 +26,12 @@ #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; diff --git a/libs/virtual_display/src/win10/idd.rs b/libs/virtual_display/src/win10/idd.rs index 66a90b059..ff614c058 100644 --- a/libs/virtual_display/src/win10/idd.rs +++ b/libs/virtual_display/src/win10/idd.rs @@ -206,7 +206,7 @@ extern "C" { extern "C" { pub fn Uninstall(fullInfPath: LPCTSTR, rebootRequired: PBOOL) -> BOOL; - pub fn MonitorPlugIn(index: UINT, retries: INT) -> 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; diff --git a/libs/virtual_display/src/win10/mod.rs b/libs/virtual_display/src/win10/mod.rs index d8797d709..549c9a807 100644 --- a/libs/virtual_display/src/win10/mod.rs +++ b/libs/virtual_display/src/win10/mod.rs @@ -1 +1,4 @@ -pub mod idd; \ No newline at end of file +pub mod idd; + +pub const DRIVER_INSTALL_PATH: &str = "RustDeskIddDriver/RustDeskIddDriver.inf"; +pub const DRIVER_DOWNLOAD_URL: &str = ""; diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 68faf178e..5b272273a 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"; @@ -446,6 +447,12 @@ fn get_current_display() -> ResultType<(usize, usize, Display)> { let mut current = *CURRENT_DISPLAY.lock().unwrap() as usize; let mut displays = Display::all()?; if displays.len() == 0 { + // try plugin monitor + if virtual_display::is_device_created() { + if let Err(e) = virtual_display::plug_in_monitor() { + log::error!("plug in monitor failed {}", e) + } + } bail!("No displays"); } let n = displays.len(); diff --git a/src/ui.rs b/src/ui.rs index c59f01299..6c69f14b5 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,18 @@ impl UI { } } + // TODO: ui prompt + fn install_virtual_display(&self) { + match virtual_display::install_update_driver() { + Ok(_) => { + log::info!("Virtual Display: install virtual display done"); + } + 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 +703,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") { From 1eb696356e9f7634f08b96f148f3d90e1846a8ea Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 12 Mar 2022 20:18:57 +0800 Subject: [PATCH 3/7] virtual display: idd add uninstall && example1 Signed-off-by: fufesou --- .../examples/idd_controller.rs | 2 +- .../examples/virtual_display_1.rs | 89 +++++++++++++++++++ libs/virtual_display/src/lib.rs | 40 ++++++++- src/ui.rs | 8 +- 4 files changed, 133 insertions(+), 6 deletions(-) create mode 100644 libs/virtual_display/examples/virtual_display_1.rs diff --git a/libs/virtual_display/examples/idd_controller.rs b/libs/virtual_display/examples/idd_controller.rs index 0bcb79d66..98e2c2b44 100644 --- a/libs/virtual_display/examples/idd_controller.rs +++ b/libs/virtual_display/examples/idd_controller.rs @@ -104,7 +104,7 @@ fn main() { println!("Create device begin"); if h_sw_device != invalid_device { println!("Device created before"); - break; + continue; } if idd::FALSE == idd::DeviceCreate(&mut h_sw_device) { println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap()); 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 index 2a37fb586..03b3b59e7 100644 --- a/libs/virtual_display/src/lib.rs +++ b/libs/virtual_display/src/lib.rs @@ -24,7 +24,7 @@ pub fn download_driver() -> ResultType<()> { Ok(()) } -pub fn install_update_driver() -> ResultType<()> { +pub fn install_update_driver(reboot_required: &mut bool) -> ResultType<()> { #[cfg(windows)] let install_path = win10::DRIVER_INSTALL_PATH; #[cfg(not(windows))] @@ -43,12 +43,45 @@ pub fn install_update_driver() -> ResultType<()> { unsafe { #[cfg(windows)] { - let mut reboot_required = win10::idd::FALSE; - if win10::idd::InstallUpdate(full_install_path, &mut reboot_required) + let mut reboot_required_tmp = win10::idd::FALSE; + if win10::idd::InstallUpdate(full_install_path, &mut reboot_required_tmp) == win10::idd::FALSE { bail!("{}", CStr::from_ptr(win10::idd::GetLastMsg()).to_str()?); } + *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), + }; + + unsafe { + #[cfg(windows)] + { + let mut reboot_required_tmp = win10::idd::FALSE; + if win10::idd::Uninstall(full_install_path, &mut reboot_required_tmp) + == win10::idd::FALSE + { + bail!("{}", CStr::from_ptr(win10::idd::GetLastMsg()).to_str()?); + } + *reboot_required = reboot_required_tmp == win10::idd::TRUE; } } @@ -79,6 +112,7 @@ pub fn create_device() -> ResultType<()> { pub fn close_device() { unsafe { win10::idd::DeviceClose(*H_SW_DEVICE.lock().unwrap() as win10::idd::HSWDEVICE); + *H_SW_DEVICE.lock().unwrap() = 0; } } diff --git a/src/ui.rs b/src/ui.rs index 6c69f14b5..cc6347b84 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -367,9 +367,13 @@ impl UI { // TODO: ui prompt fn install_virtual_display(&self) { - match virtual_display::install_update_driver() { + let mut reboot_required = false; + match virtual_display::install_update_driver(&mut reboot_required) { Ok(_) => { - log::info!("Virtual Display: install virtual display done"); + 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); From 957eabcef02ba913baaf39dd813a3e4c1a2dc785 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 13 Mar 2022 16:42:31 +0800 Subject: [PATCH 4/7] virtual display: win10 debug windows. Signed-off-by: fufesou --- libs/virtual_display/src/lib.rs | 3 +++ src/server/video_service.rs | 43 ++++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/libs/virtual_display/src/lib.rs b/libs/virtual_display/src/lib.rs index 03b3b59e7..d4cb92a8b 100644 --- a/libs/virtual_display/src/lib.rs +++ b/libs/virtual_display/src/lib.rs @@ -97,6 +97,9 @@ pub fn is_device_created() -> bool { #[cfg(windows)] pub fn create_device() -> ResultType<()> { + if is_device_created() { + return Ok(()); + } unsafe { let mut h_sw_device = *H_SW_DEVICE.lock().unwrap() as win10::idd::HSWDEVICE; if win10::idd::DeviceCreate(&mut h_sw_device) == win10::idd::FALSE { diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 5b272273a..59648e205 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -122,9 +122,42 @@ impl VideoFrameController { } } -pub fn new() -> GenericService { - let sp = GenericService::new(NAME, true); - sp.run(run); +#[derive(Clone)] +pub struct VideoService(GenericService); + +impl Service for VideoService { + fn name(&self) -> &'static str { + self.0.name() + } + fn on_subscribe(&self, sub: ConnInner) { + self.0.on_subscribe(sub) + } + fn on_unsubscribe(&self, id: i32) { + self.0.on_unsubscribe(id) + } + fn is_subed(&self, id: i32) -> bool { + self.0.is_subed(id) + } + fn join(&self) { + self.0.join() + } +} + +impl Drop for VideoService { + fn drop(&mut self) { + #[cfg(windows)] + virtual_display::close_device() + } +} + +pub fn new() -> VideoService { + let sp = VideoService(GenericService::new(NAME, true)); + #[cfg(windows)] + if let Err(e) = virtual_display::create_device() { + log::error!("create device failed {}", e); + } + + sp.0.run(run); sp } @@ -448,13 +481,15 @@ fn get_current_display() -> ResultType<(usize, usize, Display)> { let mut displays = Display::all()?; if displays.len() == 0 { // try plugin monitor + #[cfg(windows)] if virtual_display::is_device_created() { if let Err(e) = virtual_display::plug_in_monitor() { - log::error!("plug in monitor failed {}", e) + log::error!("plug in monitor failed {}", e); } } bail!("No displays"); } + let n = displays.len(); if current >= n { current = 0; From 974c259a3d367d533b24e95404ed752a6f2a8dbf Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 13 Mar 2022 17:08:33 +0800 Subject: [PATCH 5/7] virtual display: win10 trivial refactor Signed-off-by: fufesou --- libs/virtual_display/src/lib.rs | 37 +++++++++++-------------- libs/virtual_display/src/win10/mod.rs | 5 ++++ src/server/video_service.rs | 39 +++++++++++++++++++++------ 3 files changed, 52 insertions(+), 29 deletions(-) diff --git a/libs/virtual_display/src/lib.rs b/libs/virtual_display/src/lib.rs index d4cb92a8b..e9dc32fe7 100644 --- a/libs/virtual_display/src/lib.rs +++ b/libs/virtual_display/src/lib.rs @@ -2,14 +2,9 @@ pub mod win10; use hbb_common::{bail, lazy_static, ResultType}; -use std::{ - ffi::{CStr, CString}, - path::Path, - sync::Mutex, -}; +use std::{ffi::CString, path::Path, sync::Mutex}; lazy_static::lazy_static! { - #[cfg(windows)] static ref H_SW_DEVICE: Mutex = Mutex::new(0); } @@ -24,7 +19,7 @@ pub fn download_driver() -> ResultType<()> { Ok(()) } -pub fn install_update_driver(reboot_required: &mut bool) -> ResultType<()> { +pub fn install_update_driver(_reboot_required: &mut bool) -> ResultType<()> { #[cfg(windows)] let install_path = win10::DRIVER_INSTALL_PATH; #[cfg(not(windows))] @@ -35,28 +30,28 @@ pub fn install_update_driver(reboot_required: &mut bool) -> ResultType<()> { bail!("{} not exists", install_path) } - let full_install_path = match abs_path.to_str() { + 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 { - #[cfg(windows)] { let mut reboot_required_tmp = win10::idd::FALSE; - if win10::idd::InstallUpdate(full_install_path, &mut reboot_required_tmp) + if win10::idd::InstallUpdate(_full_install_path, &mut reboot_required_tmp) == win10::idd::FALSE { - bail!("{}", CStr::from_ptr(win10::idd::GetLastMsg()).to_str()?); + bail!("{}", win10::get_last_msg()?); } - *reboot_required = reboot_required_tmp == win10::idd::TRUE; + *_reboot_required = reboot_required_tmp == win10::idd::TRUE; } } Ok(()) } -pub fn uninstall_driver(reboot_required: &mut bool) -> ResultType<()> { +pub fn uninstall_driver(_reboot_required: &mut bool) -> ResultType<()> { #[cfg(windows)] let install_path = win10::DRIVER_INSTALL_PATH; #[cfg(not(windows))] @@ -67,21 +62,21 @@ pub fn uninstall_driver(reboot_required: &mut bool) -> ResultType<()> { bail!("{} not exists", install_path) } - let full_install_path = match abs_path.to_str() { + 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 { - #[cfg(windows)] { let mut reboot_required_tmp = win10::idd::FALSE; - if win10::idd::Uninstall(full_install_path, &mut reboot_required_tmp) + if win10::idd::Uninstall(_full_install_path, &mut reboot_required_tmp) == win10::idd::FALSE { - bail!("{}", CStr::from_ptr(win10::idd::GetLastMsg()).to_str()?); + bail!("{}", win10::get_last_msg()?); } - *reboot_required = reboot_required_tmp == win10::idd::TRUE; + *_reboot_required = reboot_required_tmp == win10::idd::TRUE; } } @@ -103,7 +98,7 @@ pub fn create_device() -> ResultType<()> { unsafe { let mut h_sw_device = *H_SW_DEVICE.lock().unwrap() as win10::idd::HSWDEVICE; if win10::idd::DeviceCreate(&mut h_sw_device) == win10::idd::FALSE { - bail!("{}", CStr::from_ptr(win10::idd::GetLastMsg()).to_str()?); + bail!("{}", win10::get_last_msg()?); } else { *H_SW_DEVICE.lock().unwrap() = h_sw_device as u64; Ok(()) @@ -123,7 +118,7 @@ pub fn close_device() { pub fn plug_in_monitor() -> ResultType<()> { unsafe { if win10::idd::MonitorPlugIn(0, 0, 30) == win10::idd::FALSE { - bail!("{}", CStr::from_ptr(win10::idd::GetLastMsg()).to_str()?); + bail!("{}", win10::get_last_msg()?); } Ok(()) } @@ -133,7 +128,7 @@ pub fn plug_in_monitor() -> ResultType<()> { pub fn plug_out_monitor() -> ResultType<()> { unsafe { if win10::idd::MonitorPlugOut(0) == win10::idd::FALSE { - bail!("{}", CStr::from_ptr(win10::idd::GetLastMsg()).to_str()?); + bail!("{}", win10::get_last_msg()?); } Ok(()) } diff --git a/libs/virtual_display/src/win10/mod.rs b/libs/virtual_display/src/win10/mod.rs index 549c9a807..7b787cafb 100644 --- a/libs/virtual_display/src/win10/mod.rs +++ b/libs/virtual_display/src/win10/mod.rs @@ -1,4 +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 59648e205..95e7f5aae 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}, }; +#[cfg(windows)] use virtual_display; const WAIT_BASE: i32 = 17; @@ -104,7 +105,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); @@ -150,13 +151,35 @@ impl Drop for VideoService { } } -pub fn new() -> VideoService { - let sp = VideoService(GenericService::new(NAME, true)); - #[cfg(windows)] - if let Err(e) = virtual_display::create_device() { - log::error!("create device failed {}", e); - } +impl VideoService { + fn new() -> Self { + #[cfg(windows)] + if let Err(e) = virtual_display::create_device() { + log::error!("Create device failed {}", e); + } + #[cfg(windows)] + match Display::all() { + Ok(displays) => { + if displays.len() == 0 { + log::info!("Detect no displays, try create monitor"); + if virtual_display::is_device_created() { + if let Err(e) = virtual_display::plug_in_monitor() { + log::error!("Plug in monitor failed {}", e); + } + } + } + } + Err(e) => { + log::error!("Get all displays failed {}", e) + } + } + VideoService(GenericService::new(NAME, true)) + } +} + +pub fn new() -> VideoService { + let sp = VideoService::new(); sp.0.run(run); sp } @@ -484,7 +507,7 @@ fn get_current_display() -> ResultType<(usize, usize, Display)> { #[cfg(windows)] if virtual_display::is_device_created() { if let Err(e) = virtual_display::plug_in_monitor() { - log::error!("plug in monitor failed {}", e); + log::error!("Plug in monitor failed {}", e); } } bail!("No displays"); From 117bbb340919db19593113433c5e0bca3c86167f Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 15 Mar 2022 00:29:07 +0800 Subject: [PATCH 6/7] virtual display: plugout monitor on disconnecting, debug failed, may crash... Signed-off-by: fufesou --- libs/virtual_display/Cargo.toml | 2 +- libs/virtual_display/README.md | 4 +- libs/virtual_display/src/lib.rs | 61 ++++++++++++++++--- src/server/video_service.rs | 104 ++++++++++++-------------------- 4 files changed, 92 insertions(+), 79 deletions(-) diff --git a/libs/virtual_display/Cargo.toml b/libs/virtual_display/Cargo.toml index 0f7535d46..8d0b65171 100644 --- a/libs/virtual_display/Cargo.toml +++ b/libs/virtual_display/Cargo.toml @@ -13,4 +13,4 @@ thiserror = "1.0.30" lazy_static = "1.4" serde = "1.0" serde_derive = "1.0" -hbb_common = { path = "../hbb_common" } \ No newline at end of file +hbb_common = { path = "../hbb_common" } diff --git a/libs/virtual_display/README.md b/libs/virtual_display/README.md index dbc4ac6bd..d5a1a0862 100644 --- a/libs/virtual_display/README.md +++ b/libs/virtual_display/README.md @@ -10,7 +10,7 @@ Virtual display may be used on computers that do not have a monitor. Win10 provides [Indirect Display Driver Model](https://msdn.microsoft.com/en-us/library/windows/hardware/mt761968(v=vs.85).aspx). -This lib use [this project](https://github.com/fufesou/RustDeskIddDriver) as the driver. +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). @@ -19,7 +19,7 @@ This lib use [this project](https://github.com/fufesou/RustDeskIddDriver) as the #### tested platforms - [x] 19041 - +- [x] 19043 ### win7 diff --git a/libs/virtual_display/src/lib.rs b/libs/virtual_display/src/lib.rs index e9dc32fe7..237e2cf15 100644 --- a/libs/virtual_display/src/lib.rs +++ b/libs/virtual_display/src/lib.rs @@ -6,6 +6,7 @@ use std::{ffi::CString, path::Path, sync::Mutex}; lazy_static::lazy_static! { static ref H_SW_DEVICE: Mutex = Mutex::new(0); + static ref MONITOR_PLUGIN: Mutex> = Mutex::new(Vec::new()); } pub fn download_driver() -> ResultType<()> { @@ -90,46 +91,88 @@ pub fn is_device_created() -> bool { return false; } -#[cfg(windows)] pub fn create_device() -> ResultType<()> { if is_device_created() { return Ok(()); } + #[cfg(windows)] unsafe { let mut h_sw_device = *H_SW_DEVICE.lock().unwrap() as win10::idd::HSWDEVICE; if win10::idd::DeviceCreate(&mut h_sw_device) == win10::idd::FALSE { bail!("{}", win10::get_last_msg()?); } else { *H_SW_DEVICE.lock().unwrap() = h_sw_device as u64; - Ok(()) } } + Ok(()) } -#[cfg(windows)] 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; } } -#[cfg(windows)] pub fn plug_in_monitor() -> ResultType<()> { + #[cfg(windows)] unsafe { - if win10::idd::MonitorPlugIn(0, 0, 30) == win10::idd::FALSE { + let monitor_index = 0 as u32; + let plug_in_monitors = &mut *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()?); } - Ok(()) + (*plug_in_monitors).push(monitor_index); } + Ok(()) } -#[cfg(windows)] pub fn plug_out_monitor() -> ResultType<()> { + #[cfg(windows)] unsafe { - if win10::idd::MonitorPlugOut(0) == win10::idd::FALSE { + let monitor_index = 0 as u32; + if win10::idd::MonitorPlugOut(monitor_index) == win10::idd::FALSE { bail!("{}", win10::get_last_msg()?); } - Ok(()) + let plug_in_monitors = &mut *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/src/server/video_service.rs b/src/server/video_service.rs index 95e7f5aae..429cff6ce 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -32,7 +32,6 @@ use std::{ io::ErrorKind::WouldBlock, time::{self, Duration, Instant}, }; -#[cfg(windows)] use virtual_display; const WAIT_BASE: i32 = 17; @@ -123,64 +122,9 @@ impl VideoFrameController { } } -#[derive(Clone)] -pub struct VideoService(GenericService); - -impl Service for VideoService { - fn name(&self) -> &'static str { - self.0.name() - } - fn on_subscribe(&self, sub: ConnInner) { - self.0.on_subscribe(sub) - } - fn on_unsubscribe(&self, id: i32) { - self.0.on_unsubscribe(id) - } - fn is_subed(&self, id: i32) -> bool { - self.0.is_subed(id) - } - fn join(&self) { - self.0.join() - } -} - -impl Drop for VideoService { - fn drop(&mut self) { - #[cfg(windows)] - virtual_display::close_device() - } -} - -impl VideoService { - fn new() -> Self { - #[cfg(windows)] - if let Err(e) = virtual_display::create_device() { - log::error!("Create device failed {}", e); - } - #[cfg(windows)] - match Display::all() { - Ok(displays) => { - if displays.len() == 0 { - log::info!("Detect no displays, try create monitor"); - if virtual_display::is_device_created() { - if let Err(e) = virtual_display::plug_in_monitor() { - log::error!("Plug in monitor failed {}", e); - } - } - } - } - Err(e) => { - log::error!("Get all displays failed {}", e) - } - } - - VideoService(GenericService::new(NAME, true)) - } -} - -pub fn new() -> VideoService { - let sp = VideoService::new(); - sp.0.run(run); +pub fn new() -> GenericService { + let sp = GenericService::new(NAME, true); + sp.run(run); sp } @@ -190,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, }; @@ -355,6 +299,11 @@ fn run(sp: GenericService) -> ResultType<()> { std::thread::sleep(spf - elapsed); } } + + // Close device, if there are no connections + if virtual_display::is_device_created() { + virtual_display::close_device() + } Ok(()) } @@ -436,7 +385,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 @@ -450,7 +399,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; } @@ -485,7 +434,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; @@ -499,17 +448,38 @@ pub fn switch_to_primary() { switch_display(get_primary() as _); } -fn get_current_display() -> ResultType<(usize, usize, Display)> { - let mut current = *CURRENT_DISPLAY.lock().unwrap() as usize; +fn try_get_displays() -> ResultType> { let mut displays = Display::all()?; if displays.len() == 0 { - // try plugin monitor - #[cfg(windows)] + // Try plugin monitor + if !virtual_display::is_device_created() { + if let Err(e) = virtual_display::create_device() { + log::error!("Create device failed {}", e); + } + } if virtual_display::is_device_created() { if let Err(e) = virtual_display::plug_in_monitor() { log::error!("Plug in monitor failed {}", e); + } else { + if let Err(e) = virtual_display::update_monitor_modes() { + log::error!("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 = try_get_displays()?; + if displays.len() == 0 { bail!("No displays"); } From 561a0d20ed1e086d49eef763bfbeff51638a8cef Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 17 Mar 2022 20:35:39 +0800 Subject: [PATCH 7/7] virtual display: win10 auto install and uninstall virtual display Signed-off-by: fufesou --- libs/virtual_display/src/lib.rs | 12 ++++++++---- src/server/video_service.rs | 23 ++++++++++++++++------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/libs/virtual_display/src/lib.rs b/libs/virtual_display/src/lib.rs index 237e2cf15..73b4bdbd4 100644 --- a/libs/virtual_display/src/lib.rs +++ b/libs/virtual_display/src/lib.rs @@ -5,6 +5,8 @@ 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()); } @@ -97,11 +99,12 @@ pub fn create_device() -> ResultType<()> { } #[cfg(windows)] unsafe { - let mut h_sw_device = *H_SW_DEVICE.lock().unwrap() as win10::idd::HSWDEVICE; + 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 { - *H_SW_DEVICE.lock().unwrap() = h_sw_device as u64; + *lock_device = h_sw_device as u64; } } Ok(()) @@ -112,6 +115,7 @@ 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(); } } @@ -119,7 +123,7 @@ pub fn plug_in_monitor() -> ResultType<()> { #[cfg(windows)] unsafe { let monitor_index = 0 as u32; - let plug_in_monitors = &mut *MONITOR_PLUGIN.lock().unwrap(); + 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 { @@ -142,7 +146,7 @@ pub fn plug_out_monitor() -> ResultType<()> { if win10::idd::MonitorPlugOut(monitor_index) == win10::idd::FALSE { bail!("{}", win10::get_last_msg()?); } - let plug_in_monitors = &mut *MONITOR_PLUGIN.lock().unwrap(); + 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 { diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 429cff6ce..dd7f78d37 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -159,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()?; @@ -300,10 +312,6 @@ fn run(sp: GenericService) -> ResultType<()> { } } - // Close device, if there are no connections - if virtual_display::is_device_created() { - virtual_display::close_device() - } Ok(()) } @@ -451,18 +459,19 @@ pub fn switch_to_primary() { 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::error!("Create device failed {}", e); + log::debug!("Create device failed {}", e); } } if virtual_display::is_device_created() { if let Err(e) = virtual_display::plug_in_monitor() { - log::error!("Plug in monitor failed {}", e); + log::debug!("Plug in monitor failed {}", e); } else { if let Err(e) = virtual_display::update_monitor_modes() { - log::error!("Update monitor modes failed {}", e); + log::debug!("Update monitor modes failed {}", e); } } }