mirror of
https://github.com/rustdesk/rustdesk.git
synced 2025-01-18 15:53:00 +08:00
commit
b31eea0279
13
Cargo.lock
generated
13
Cargo.lock
generated
@ -3444,6 +3444,7 @@ dependencies = [
|
|||||||
"sysinfo",
|
"sysinfo",
|
||||||
"systray",
|
"systray",
|
||||||
"uuid",
|
"uuid",
|
||||||
|
"virtual_display",
|
||||||
"whoami",
|
"whoami",
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
"windows-service",
|
"windows-service",
|
||||||
@ -4180,6 +4181,18 @@ version = "0.9.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "virtual_display"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"hbb_common",
|
||||||
|
"lazy_static",
|
||||||
|
"serde 1.0.136",
|
||||||
|
"serde_derive",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "walkdir"
|
name = "walkdir"
|
||||||
version = "2.3.2"
|
version = "2.3.2"
|
||||||
|
@ -22,6 +22,7 @@ scrap = { path = "libs/scrap" }
|
|||||||
hbb_common = { path = "libs/hbb_common" }
|
hbb_common = { path = "libs/hbb_common" }
|
||||||
enigo = { path = "libs/enigo" }
|
enigo = { path = "libs/enigo" }
|
||||||
clipboard = { path = "libs/clipboard" }
|
clipboard = { path = "libs/clipboard" }
|
||||||
|
virtual_display = { path = "libs/virtual_display" }
|
||||||
sys-locale = "0.1"
|
sys-locale = "0.1"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
@ -83,7 +84,7 @@ rust-pulsectl = { git = "https://github.com/open-trade/pulsectl" }
|
|||||||
android_logger = "0.10"
|
android_logger = "0.10"
|
||||||
|
|
||||||
[workspace]
|
[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]
|
[package.metadata.winres]
|
||||||
LegalCopyright = "Copyright © 2020"
|
LegalCopyright = "Copyright © 2020"
|
||||||
|
@ -27,13 +27,14 @@ pub use anyhow::{self, bail};
|
|||||||
pub use futures_util;
|
pub use futures_util;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod fs;
|
pub mod fs;
|
||||||
|
pub use lazy_static;
|
||||||
|
pub use mac_address;
|
||||||
pub use rand;
|
pub use rand;
|
||||||
pub use regex;
|
pub use regex;
|
||||||
pub use sodiumoxide;
|
pub use sodiumoxide;
|
||||||
pub use tokio_socks;
|
pub use tokio_socks;
|
||||||
pub use tokio_socks::IntoTargetAddr;
|
pub use tokio_socks::IntoTargetAddr;
|
||||||
pub use tokio_socks::TargetAddr;
|
pub use tokio_socks::TargetAddr;
|
||||||
pub use mac_address;
|
|
||||||
|
|
||||||
#[cfg(feature = "quic")]
|
#[cfg(feature = "quic")]
|
||||||
pub type Stream = quic::Connection;
|
pub type Stream = quic::Connection;
|
||||||
|
16
libs/virtual_display/Cargo.toml
Normal file
16
libs/virtual_display/Cargo.toml
Normal file
@ -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" }
|
32
libs/virtual_display/README.md
Normal file
32
libs/virtual_display/README.md
Normal file
@ -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
|
35
libs/virtual_display/build.rs
Normal file
35
libs/virtual_display/build.rs
Normal file
@ -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();
|
||||||
|
}
|
138
libs/virtual_display/examples/idd_controller.rs
Normal file
138
libs/virtual_display/examples/idd_controller.rs
Normal file
@ -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<idd::MonitorMode> = 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);
|
||||||
|
}
|
||||||
|
}
|
89
libs/virtual_display/examples/virtual_display_1.rs
Normal file
89
libs/virtual_display/examples/virtual_display_1.rs
Normal file
@ -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(),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
182
libs/virtual_display/src/lib.rs
Normal file
182
libs/virtual_display/src/lib.rs
Normal file
@ -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<u64> = Mutex::new(0);
|
||||||
|
static ref MONITOR_PLUGIN: Mutex<Vec<u32>> = 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(())
|
||||||
|
}
|
784
libs/virtual_display/src/win10/IddController.c
Normal file
784
libs/virtual_display/src/win10/IddController.c
Normal file
@ -0,0 +1,784 @@
|
|||||||
|
#include "./IddController.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <newdev.h>
|
||||||
|
#include <swdevice.h>
|
||||||
|
#include <strsafe.h>
|
||||||
|
#include <cfgmgr32.h>
|
||||||
|
#include <combaseapi.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
144
libs/virtual_display/src/win10/IddController.h
Normal file
144
libs/virtual_display/src/win10/IddController.h
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <tchar.h>
|
||||||
|
#include <swdevice.h>
|
||||||
|
|
||||||
|
#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
|
54
libs/virtual_display/src/win10/Public.h
Normal file
54
libs/virtual_display/src/win10/Public.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <minwindef.h>
|
||||||
|
#include <winioctl.h>
|
||||||
|
#include <guiddef.h>
|
||||||
|
|
||||||
|
#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"
|
||||||
|
|
215
libs/virtual_display/src/win10/idd.rs
Normal file
215
libs/virtual_display/src/win10/idd.rs
Normal file
@ -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);
|
||||||
|
}
|
9
libs/virtual_display/src/win10/mod.rs
Normal file
9
libs/virtual_display/src/win10/mod.rs
Normal file
@ -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()
|
||||||
|
}
|
@ -32,6 +32,7 @@ use std::{
|
|||||||
io::ErrorKind::WouldBlock,
|
io::ErrorKind::WouldBlock,
|
||||||
time::{self, Duration, Instant},
|
time::{self, Duration, Instant},
|
||||||
};
|
};
|
||||||
|
use virtual_display;
|
||||||
|
|
||||||
const WAIT_BASE: i32 = 17;
|
const WAIT_BASE: i32 = 17;
|
||||||
pub const NAME: &'static str = "video";
|
pub const NAME: &'static str = "video";
|
||||||
@ -103,7 +104,7 @@ impl VideoFrameController {
|
|||||||
}
|
}
|
||||||
Ok(Some((id, instant))) => {
|
Ok(Some((id, instant))) => {
|
||||||
if let Some(tm) = 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);
|
fetched_conn_ids.insert(id);
|
||||||
|
|
||||||
@ -133,7 +134,7 @@ fn check_display_changed(
|
|||||||
last_width: usize,
|
last_width: usize,
|
||||||
last_hegiht: usize,
|
last_hegiht: usize,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let displays = match Display::all() {
|
let displays = match try_get_displays() {
|
||||||
Ok(d) => d,
|
Ok(d) => d,
|
||||||
_ => return false,
|
_ => return false,
|
||||||
};
|
};
|
||||||
@ -158,6 +159,18 @@ fn check_display_changed(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run(sp: GenericService) -> ResultType<()> {
|
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 fps = 30;
|
||||||
let spf = time::Duration::from_secs_f32(1. / (fps as f32));
|
let spf = time::Duration::from_secs_f32(1. / (fps as f32));
|
||||||
let (ndisplay, current, display) = get_current_display()?;
|
let (ndisplay, current, display) = get_current_display()?;
|
||||||
@ -298,6 +311,7 @@ fn run(sp: GenericService) -> ResultType<()> {
|
|||||||
std::thread::sleep(spf - elapsed);
|
std::thread::sleep(spf - elapsed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,7 +393,7 @@ fn handle_one_frame(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_display_num() -> usize {
|
fn get_display_num() -> usize {
|
||||||
if let Ok(d) = Display::all() {
|
if let Ok(d) = try_get_displays() {
|
||||||
d.len()
|
d.len()
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
@ -393,7 +407,7 @@ pub fn get_displays() -> ResultType<(usize, Vec<DisplayInfo>)> {
|
|||||||
}
|
}
|
||||||
let mut displays = Vec::new();
|
let mut displays = Vec::new();
|
||||||
let mut primary = 0;
|
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() {
|
if d.is_primary() {
|
||||||
primary = i;
|
primary = i;
|
||||||
}
|
}
|
||||||
@ -428,7 +442,7 @@ pub fn refresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_primary() -> usize {
|
fn get_primary() -> usize {
|
||||||
if let Ok(all) = Display::all() {
|
if let Ok(all) = try_get_displays() {
|
||||||
for (i, d) in all.iter().enumerate() {
|
for (i, d) in all.iter().enumerate() {
|
||||||
if d.is_primary() {
|
if d.is_primary() {
|
||||||
return i;
|
return i;
|
||||||
@ -442,12 +456,42 @@ pub fn switch_to_primary() {
|
|||||||
switch_display(get_primary() as _);
|
switch_display(get_primary() as _);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn try_get_displays() -> ResultType<Vec<Display>> {
|
||||||
|
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)> {
|
fn get_current_display() -> ResultType<(usize, usize, Display)> {
|
||||||
let mut current = *CURRENT_DISPLAY.lock().unwrap() as usize;
|
let mut current = *CURRENT_DISPLAY.lock().unwrap() as usize;
|
||||||
let mut displays = Display::all()?;
|
let mut displays = try_get_displays()?;
|
||||||
if displays.len() == 0 {
|
if displays.len() == 0 {
|
||||||
bail!("No displays");
|
bail!("No displays");
|
||||||
}
|
}
|
||||||
|
|
||||||
let n = displays.len();
|
let n = displays.len();
|
||||||
if current >= n {
|
if current >= n {
|
||||||
current = 0;
|
current = 0;
|
||||||
|
18
src/ui.rs
18
src/ui.rs
@ -19,6 +19,7 @@ use std::{
|
|||||||
process::Child,
|
process::Child,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
use virtual_display;
|
||||||
|
|
||||||
pub type Childs = Arc<Mutex<(bool, HashMap<(String, String), Child>)>>;
|
pub type Childs = Arc<Mutex<(bool, HashMap<(String, String), Child>)>>;
|
||||||
|
|
||||||
@ -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 {
|
fn install_path(&mut self) -> String {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
return crate::platform::windows::get_install_info().1;
|
return crate::platform::windows::get_install_info().1;
|
||||||
@ -690,6 +707,7 @@ impl sciter::EventHandler for UI {
|
|||||||
fn get_sound_inputs();
|
fn get_sound_inputs();
|
||||||
fn set_options(Value);
|
fn set_options(Value);
|
||||||
fn set_option(String, String);
|
fn set_option(String, String);
|
||||||
|
fn install_virtual_display();
|
||||||
fn get_software_update_url();
|
fn get_software_update_url();
|
||||||
fn get_new_version();
|
fn get_new_version();
|
||||||
fn get_version();
|
fn get_version();
|
||||||
|
@ -168,6 +168,7 @@ class MyIdMenu: Reactor.Component {
|
|||||||
<li #custom-server>{translate('ID/Relay Server')}</li>
|
<li #custom-server>{translate('ID/Relay Server')}</li>
|
||||||
<li #whitelist title={translate('whitelist_tip')}>{translate('IP Whitelisting')}</li>
|
<li #whitelist title={translate('whitelist_tip')}>{translate('IP Whitelisting')}</li>
|
||||||
<li #socks5-server>{translate('Socks5 Proxy')}</li>
|
<li #socks5-server>{translate('Socks5 Proxy')}</li>
|
||||||
|
{is_win ? <li #install-virtual-display>Install virtual display</li> : ""}
|
||||||
<div .separator />
|
<div .separator />
|
||||||
<li #stop-service class={service_stopped ? "line-through" : "selected"}><span>{svg_checkmark}</span>{translate("Enable Service")}</li>
|
<li #stop-service class={service_stopped ? "line-through" : "selected"}><span>{svg_checkmark}</span>{translate("Enable Service")}</li>
|
||||||
<DirectServer />
|
<DirectServer />
|
||||||
@ -268,6 +269,8 @@ class MyIdMenu: Reactor.Component {
|
|||||||
}
|
}
|
||||||
handler.set_socks(proxy, username, password);
|
handler.set_socks(proxy, username, password);
|
||||||
}, 240);
|
}, 240);
|
||||||
|
} else if (me.id == "install-virtual-display") {
|
||||||
|
handler.install_virtual_display();
|
||||||
} else if (me.id == "stop-service") {
|
} else if (me.id == "stop-service") {
|
||||||
handler.set_option("stop-service", service_stopped ? "" : "Y");
|
handler.set_option("stop-service", service_stopped ? "" : "Y");
|
||||||
} else if (me.id == "about") {
|
} else if (me.id == "about") {
|
||||||
|
Loading…
Reference in New Issue
Block a user