mirror of
https://github.com/rustdesk/rustdesk.git
synced 2025-12-16 21:16:35 +00:00
refact: win, virtual display (#7767)
* refact: win, virtual display Signed-off-by: fufesou <shuanglongchen@yeah.net> * Update flutter-build.yml --------- Signed-off-by: fufesou <shuanglongchen@yeah.net> Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
This commit is contained in:
@@ -1,57 +1,47 @@
|
||||
#[cfg(target_os = "windows")]
|
||||
use hbb_common::platform::windows::is_windows_version_or_greater;
|
||||
use hbb_common::{allow_err, bail, lazy_static, log, ResultType};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use hbb_common::{bail, platform::windows::is_windows_version_or_greater, ResultType};
|
||||
use std::sync::atomic;
|
||||
|
||||
// virtual display index range: 0 - 2 are reserved for headless and other special uses.
|
||||
const VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS: u32 = 0;
|
||||
const VIRTUAL_DISPLAY_START_FOR_PEER: u32 = 1;
|
||||
const VIRTUAL_DISPLAY_MAX_COUNT: u32 = 5;
|
||||
// This string is defined here.
|
||||
// https://github.com/fufesou/RustDeskIddDriver/blob/b370aad3f50028b039aad211df60c8051c4a64d6/RustDeskIddDriver/RustDeskIddDriver.inf#LL73C1-L73C40
|
||||
pub const RUSTDESK_IDD_DEVICE_STRING: &'static str = "RustDeskIddDriver Device\0";
|
||||
pub const AMYUNI_IDD_DEVICE_STRING: &'static str = "USB Mobile Monitor Virtual Display\0";
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref VIRTUAL_DISPLAY_MANAGER: Arc<Mutex<VirtualDisplayManager>> =
|
||||
Arc::new(Mutex::new(VirtualDisplayManager::default()));
|
||||
const IDD_IMPL: &str = IDD_IMPL_AMYUNI;
|
||||
const IDD_IMPL_RUSTDESK: &str = "rustdesk_idd";
|
||||
const IDD_IMPL_AMYUNI: &str = "amyuni_idd";
|
||||
|
||||
const IS_CAN_PLUG_OUT_ALL_NOT_SET: i8 = 0;
|
||||
const IS_CAN_PLUG_OUT_ALL_YES: i8 = 1;
|
||||
const IS_CAN_PLUG_OUT_ALL_NO: i8 = 2;
|
||||
static IS_CAN_PLUG_OUT_ALL: atomic::AtomicI8 = atomic::AtomicI8::new(IS_CAN_PLUG_OUT_ALL_NOT_SET);
|
||||
|
||||
pub fn is_can_plug_out_all() -> bool {
|
||||
IS_CAN_PLUG_OUT_ALL.load(atomic::Ordering::Relaxed) != IS_CAN_PLUG_OUT_ALL_NO
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct VirtualDisplayManager {
|
||||
headless_index_name: Option<(u32, String)>,
|
||||
peer_index_name: HashMap<u32, String>,
|
||||
is_driver_installed: bool,
|
||||
// No need to consider concurrency here.
|
||||
pub fn set_can_plug_out_all(v: bool) {
|
||||
if IS_CAN_PLUG_OUT_ALL.load(atomic::Ordering::Relaxed) == IS_CAN_PLUG_OUT_ALL_NOT_SET {
|
||||
IS_CAN_PLUG_OUT_ALL.store(
|
||||
if v {
|
||||
IS_CAN_PLUG_OUT_ALL_YES
|
||||
} else {
|
||||
IS_CAN_PLUG_OUT_ALL_NO
|
||||
},
|
||||
atomic::Ordering::Relaxed,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtualDisplayManager {
|
||||
fn prepare_driver(&mut self) -> ResultType<()> {
|
||||
if !self.is_driver_installed {
|
||||
self.install_update_driver()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn is_amyuni_idd() -> bool {
|
||||
IDD_IMPL == IDD_IMPL_AMYUNI
|
||||
}
|
||||
|
||||
fn install_update_driver(&mut self) -> ResultType<()> {
|
||||
if let Err(e) = virtual_display::create_device() {
|
||||
if !e.to_string().contains("Device is already created") {
|
||||
bail!("Create device failed {}", e);
|
||||
}
|
||||
}
|
||||
// Reboot is not required for this case.
|
||||
let mut _reboot_required = false;
|
||||
virtual_display::install_update_driver(&mut _reboot_required)?;
|
||||
self.is_driver_installed = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn plug_in_monitor(index: u32, modes: &[virtual_display::MonitorMode]) -> ResultType<()> {
|
||||
if let Err(e) = virtual_display::plug_in_monitor(index) {
|
||||
bail!("Plug in monitor failed {}", e);
|
||||
}
|
||||
if let Err(e) = virtual_display::update_monitor_modes(index, &modes) {
|
||||
log::error!("Update monitor modes failed {}", e);
|
||||
}
|
||||
Ok(())
|
||||
pub fn get_cur_device_string() -> &'static str {
|
||||
match IDD_IMPL {
|
||||
IDD_IMPL_RUSTDESK => RUSTDESK_IDD_DEVICE_STRING,
|
||||
IDD_IMPL_AMYUNI => AMYUNI_IDD_DEVICE_STRING,
|
||||
_ => "",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,209 +56,514 @@ pub fn is_virtual_display_supported() -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn install_update_driver() -> ResultType<()> {
|
||||
VIRTUAL_DISPLAY_MANAGER
|
||||
.lock()
|
||||
.unwrap()
|
||||
.install_update_driver()
|
||||
}
|
||||
|
||||
pub fn plug_in_headless() -> ResultType<()> {
|
||||
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
||||
manager.prepare_driver()?;
|
||||
let modes = [virtual_display::MonitorMode {
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
sync: 60,
|
||||
}];
|
||||
let device_names = windows::get_device_names();
|
||||
VirtualDisplayManager::plug_in_monitor(VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS, &modes)?;
|
||||
let device_name = get_new_device_name(&device_names);
|
||||
manager.headless_index_name = Some((VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS, device_name));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn plug_out_headless() -> bool {
|
||||
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
||||
if let Some((index, _)) = manager.headless_index_name.take() {
|
||||
if let Err(e) = virtual_display::plug_out_monitor(index) {
|
||||
log::error!("Plug out monitor failed {}", e);
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
match IDD_IMPL {
|
||||
IDD_IMPL_RUSTDESK => rustdesk_idd::plug_in_headless(),
|
||||
IDD_IMPL_AMYUNI => amyuni_idd::plug_in_headless(),
|
||||
_ => bail!("Unsupported virtual display implementation."),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_new_device_name(device_names: &HashSet<String>) -> String {
|
||||
for _ in 0..3 {
|
||||
let device_names_af = windows::get_device_names();
|
||||
let diff_names: Vec<_> = device_names_af.difference(&device_names).collect();
|
||||
if diff_names.len() == 1 {
|
||||
return diff_names[0].clone();
|
||||
} else if diff_names.len() > 1 {
|
||||
log::error!(
|
||||
"Failed to get diff device names after plugin virtual display, more than one diff names: {:?}",
|
||||
&diff_names
|
||||
);
|
||||
return "".to_string();
|
||||
}
|
||||
// Sleep is needed here to wait for the virtual display to be ready.
|
||||
std::thread::sleep(std::time::Duration::from_millis(50));
|
||||
pub fn get_platform_additions() -> serde_json::Map<String, serde_json::Value> {
|
||||
let mut map = serde_json::Map::new();
|
||||
if !crate::platform::windows::is_self_service_running() {
|
||||
return map;
|
||||
}
|
||||
log::error!("Failed to get diff device names after plugin virtual display",);
|
||||
"".to_string()
|
||||
}
|
||||
|
||||
pub fn get_virtual_displays() -> Vec<u32> {
|
||||
VIRTUAL_DISPLAY_MANAGER
|
||||
.lock()
|
||||
.unwrap()
|
||||
.peer_index_name
|
||||
.keys()
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn plug_in_index_modes(
|
||||
idx: u32,
|
||||
mut modes: Vec<virtual_display::MonitorMode>,
|
||||
) -> ResultType<()> {
|
||||
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
||||
manager.prepare_driver()?;
|
||||
if !manager.peer_index_name.contains_key(&idx) {
|
||||
let device_names = windows::get_device_names();
|
||||
if modes.is_empty() {
|
||||
modes.push(virtual_display::MonitorMode {
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
sync: 60,
|
||||
});
|
||||
}
|
||||
match VirtualDisplayManager::plug_in_monitor(idx, modes.as_slice()) {
|
||||
Ok(_) => {
|
||||
let device_name = get_new_device_name(&device_names);
|
||||
manager.peer_index_name.insert(idx, device_name);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Plug in monitor failed {}", e);
|
||||
map.insert("idd_impl".into(), serde_json::json!(IDD_IMPL));
|
||||
match IDD_IMPL {
|
||||
IDD_IMPL_RUSTDESK => {
|
||||
let virtual_displays = rustdesk_idd::get_virtual_displays();
|
||||
if !virtual_displays.is_empty() {
|
||||
map.insert(
|
||||
"rustdesk_virtual_displays".into(),
|
||||
serde_json::json!(virtual_displays),
|
||||
);
|
||||
}
|
||||
}
|
||||
IDD_IMPL_AMYUNI => {
|
||||
let c = amyuni_idd::get_monitor_count();
|
||||
if c > 0 {
|
||||
map.insert("amyuni_virtual_displays".into(), serde_json::json!(c));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
map
|
||||
}
|
||||
|
||||
pub fn reset_all() -> ResultType<()> {
|
||||
if is_virtual_display_supported() {
|
||||
return Ok(());
|
||||
#[inline]
|
||||
pub fn plug_in_monitor(idx: u32, modes: Vec<virtual_display::MonitorMode>) -> ResultType<()> {
|
||||
match IDD_IMPL {
|
||||
IDD_IMPL_RUSTDESK => rustdesk_idd::plug_in_index_modes(idx, modes),
|
||||
IDD_IMPL_AMYUNI => amyuni_idd::plug_in_monitor(),
|
||||
_ => bail!("Unsupported virtual display implementation."),
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = plug_out_peer_request(&get_virtual_displays()) {
|
||||
log::error!("Failed to plug out virtual displays: {}", e);
|
||||
pub fn plug_out_monitor(index: i32) -> ResultType<()> {
|
||||
match IDD_IMPL {
|
||||
IDD_IMPL_RUSTDESK => {
|
||||
let indices = if index == -1 {
|
||||
rustdesk_idd::get_virtual_displays()
|
||||
} else {
|
||||
vec![index as _]
|
||||
};
|
||||
rustdesk_idd::plug_out_peer_request(&indices)
|
||||
}
|
||||
IDD_IMPL_AMYUNI => amyuni_idd::plug_out_monitor(index),
|
||||
_ => bail!("Unsupported virtual display implementation."),
|
||||
}
|
||||
let _ = plug_out_headless();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn plug_in_peer_request(modes: Vec<Vec<virtual_display::MonitorMode>>) -> ResultType<Vec<u32>> {
|
||||
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
||||
manager.prepare_driver()?;
|
||||
match IDD_IMPL {
|
||||
IDD_IMPL_RUSTDESK => rustdesk_idd::plug_in_peer_request(modes),
|
||||
IDD_IMPL_AMYUNI => {
|
||||
amyuni_idd::plug_in_monitor()?;
|
||||
Ok(vec![0])
|
||||
}
|
||||
_ => bail!("Unsupported virtual display implementation."),
|
||||
}
|
||||
}
|
||||
|
||||
let mut indices: Vec<u32> = Vec::new();
|
||||
for m in modes.iter() {
|
||||
for idx in VIRTUAL_DISPLAY_START_FOR_PEER..VIRTUAL_DISPLAY_MAX_COUNT {
|
||||
if !manager.peer_index_name.contains_key(&idx) {
|
||||
let device_names = windows::get_device_names();
|
||||
match VirtualDisplayManager::plug_in_monitor(idx, m) {
|
||||
Ok(_) => {
|
||||
let device_name = get_new_device_name(&device_names);
|
||||
manager.peer_index_name.insert(idx, device_name);
|
||||
indices.push(idx);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Plug in monitor failed {}", e);
|
||||
}
|
||||
pub fn plug_out_monitor_indices(indices: &[u32]) -> ResultType<()> {
|
||||
match IDD_IMPL {
|
||||
IDD_IMPL_RUSTDESK => rustdesk_idd::plug_out_peer_request(indices),
|
||||
IDD_IMPL_AMYUNI => {
|
||||
for _idx in indices.iter() {
|
||||
amyuni_idd::plug_out_monitor(0)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
_ => bail!("Unsupported virtual display implementation."),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_all() -> ResultType<()> {
|
||||
match IDD_IMPL {
|
||||
IDD_IMPL_RUSTDESK => rustdesk_idd::reset_all(),
|
||||
IDD_IMPL_AMYUNI => crate::privacy_mode::turn_off_privacy(0, None).unwrap_or(Ok(())),
|
||||
_ => bail!("Unsupported virtual display implementation."),
|
||||
}
|
||||
}
|
||||
|
||||
pub mod rustdesk_idd {
|
||||
use super::windows;
|
||||
use hbb_common::{allow_err, bail, lazy_static, log, ResultType};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
// virtual display index range: 0 - 2 are reserved for headless and other special uses.
|
||||
const VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS: u32 = 0;
|
||||
const VIRTUAL_DISPLAY_START_FOR_PEER: u32 = 1;
|
||||
const VIRTUAL_DISPLAY_MAX_COUNT: u32 = 5;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref VIRTUAL_DISPLAY_MANAGER: Arc<Mutex<VirtualDisplayManager>> =
|
||||
Arc::new(Mutex::new(VirtualDisplayManager::default()));
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct VirtualDisplayManager {
|
||||
headless_index_name: Option<(u32, String)>,
|
||||
peer_index_name: HashMap<u32, String>,
|
||||
is_driver_installed: bool,
|
||||
}
|
||||
|
||||
impl VirtualDisplayManager {
|
||||
fn prepare_driver(&mut self) -> ResultType<()> {
|
||||
if !self.is_driver_installed {
|
||||
self.install_update_driver()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn install_update_driver(&mut self) -> ResultType<()> {
|
||||
if let Err(e) = virtual_display::create_device() {
|
||||
if !e.to_string().contains("Device is already created") {
|
||||
bail!("Create device failed {}", e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Reboot is not required for this case.
|
||||
let mut _reboot_required = false;
|
||||
virtual_display::install_update_driver(&mut _reboot_required)?;
|
||||
self.is_driver_installed = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn plug_in_monitor(index: u32, modes: &[virtual_display::MonitorMode]) -> ResultType<()> {
|
||||
if let Err(e) = virtual_display::plug_in_monitor(index) {
|
||||
bail!("Plug in monitor failed {}", e);
|
||||
}
|
||||
if let Err(e) = virtual_display::update_monitor_modes(index, &modes) {
|
||||
log::error!("Update monitor modes failed {}", e);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn install_update_driver() -> ResultType<()> {
|
||||
VIRTUAL_DISPLAY_MANAGER
|
||||
.lock()
|
||||
.unwrap()
|
||||
.install_update_driver()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_device_names() -> Vec<String> {
|
||||
windows::get_device_names(Some(super::RUSTDESK_IDD_DEVICE_STRING))
|
||||
}
|
||||
|
||||
pub fn plug_in_headless() -> ResultType<()> {
|
||||
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
||||
manager.prepare_driver()?;
|
||||
let modes = [virtual_display::MonitorMode {
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
sync: 60,
|
||||
}];
|
||||
let device_names = get_device_names().into_iter().collect();
|
||||
VirtualDisplayManager::plug_in_monitor(VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS, &modes)?;
|
||||
let device_name = get_new_device_name(&device_names);
|
||||
manager.headless_index_name = Some((VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS, device_name));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn plug_out_headless() -> bool {
|
||||
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
||||
if let Some((index, _)) = manager.headless_index_name.take() {
|
||||
if let Err(e) = virtual_display::plug_out_monitor(index) {
|
||||
log::error!("Plug out monitor failed {}", e);
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn get_new_device_name(device_names: &HashSet<String>) -> String {
|
||||
for _ in 0..3 {
|
||||
let device_names_af: HashSet<String> = get_device_names().into_iter().collect();
|
||||
let diff_names: Vec<_> = device_names_af.difference(&device_names).collect();
|
||||
if diff_names.len() == 1 {
|
||||
return diff_names[0].clone();
|
||||
} else if diff_names.len() > 1 {
|
||||
log::error!(
|
||||
"Failed to get diff device names after plugin virtual display, more than one diff names: {:?}",
|
||||
&diff_names
|
||||
);
|
||||
return "".to_string();
|
||||
}
|
||||
// Sleep is needed here to wait for the virtual display to be ready.
|
||||
std::thread::sleep(std::time::Duration::from_millis(50));
|
||||
}
|
||||
log::error!("Failed to get diff device names after plugin virtual display",);
|
||||
"".to_string()
|
||||
}
|
||||
|
||||
pub fn get_virtual_displays() -> Vec<u32> {
|
||||
VIRTUAL_DISPLAY_MANAGER
|
||||
.lock()
|
||||
.unwrap()
|
||||
.peer_index_name
|
||||
.keys()
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn plug_in_index_modes(
|
||||
idx: u32,
|
||||
mut modes: Vec<virtual_display::MonitorMode>,
|
||||
) -> ResultType<()> {
|
||||
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
||||
manager.prepare_driver()?;
|
||||
if !manager.peer_index_name.contains_key(&idx) {
|
||||
let device_names = get_device_names().into_iter().collect();
|
||||
if modes.is_empty() {
|
||||
modes.push(virtual_display::MonitorMode {
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
sync: 60,
|
||||
});
|
||||
}
|
||||
match VirtualDisplayManager::plug_in_monitor(idx, modes.as_slice()) {
|
||||
Ok(_) => {
|
||||
let device_name = get_new_device_name(&device_names);
|
||||
manager.peer_index_name.insert(idx, device_name);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Plug in monitor failed {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn reset_all() -> ResultType<()> {
|
||||
if super::is_virtual_display_supported() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Err(e) = plug_out_peer_request(&get_virtual_displays()) {
|
||||
log::error!("Failed to plug out virtual displays: {}", e);
|
||||
}
|
||||
let _ = plug_out_headless();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn plug_in_peer_request(
|
||||
modes: Vec<Vec<virtual_display::MonitorMode>>,
|
||||
) -> ResultType<Vec<u32>> {
|
||||
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
||||
manager.prepare_driver()?;
|
||||
|
||||
let mut indices: Vec<u32> = Vec::new();
|
||||
for m in modes.iter() {
|
||||
for idx in VIRTUAL_DISPLAY_START_FOR_PEER..VIRTUAL_DISPLAY_MAX_COUNT {
|
||||
if !manager.peer_index_name.contains_key(&idx) {
|
||||
let device_names = get_device_names().into_iter().collect();
|
||||
match VirtualDisplayManager::plug_in_monitor(idx, m) {
|
||||
Ok(_) => {
|
||||
let device_name = get_new_device_name(&device_names);
|
||||
manager.peer_index_name.insert(idx, device_name);
|
||||
indices.push(idx);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Plug in monitor failed {}", e);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(indices)
|
||||
}
|
||||
|
||||
pub fn plug_out_peer_request(indices: &[u32]) -> ResultType<()> {
|
||||
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
||||
for idx in indices.iter() {
|
||||
if manager.peer_index_name.contains_key(idx) {
|
||||
allow_err!(virtual_display::plug_out_monitor(*idx));
|
||||
manager.peer_index_name.remove(idx);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_virtual_display(name: &str) -> bool {
|
||||
let lock = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
||||
if let Some((_, device_name)) = &lock.headless_index_name {
|
||||
if windows::is_device_name(device_name, name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (_, v) in lock.peer_index_name.iter() {
|
||||
if windows::is_device_name(v, name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn change_resolution(index: u32, w: u32, h: u32) -> bool {
|
||||
let modes = [virtual_display::MonitorMode {
|
||||
width: w,
|
||||
height: h,
|
||||
sync: 60,
|
||||
}];
|
||||
match virtual_display::update_monitor_modes(index, &modes) {
|
||||
Ok(_) => true,
|
||||
Err(e) => {
|
||||
log::error!("Update monitor {} modes {:?} failed: {}", index, &modes, e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(indices)
|
||||
}
|
||||
pub fn change_resolution_if_is_virtual_display(name: &str, w: u32, h: u32) -> Option<bool> {
|
||||
let lock = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
||||
if let Some((index, device_name)) = &lock.headless_index_name {
|
||||
if windows::is_device_name(device_name, name) {
|
||||
return Some(change_resolution(*index, w, h));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn plug_out_peer_request(indices: &[u32]) -> ResultType<()> {
|
||||
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
||||
for idx in indices.iter() {
|
||||
if manager.peer_index_name.contains_key(idx) {
|
||||
allow_err!(virtual_display::plug_out_monitor(*idx));
|
||||
manager.peer_index_name.remove(idx);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_virtual_display(name: &str) -> bool {
|
||||
let lock = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
||||
if let Some((_, device_name)) = &lock.headless_index_name {
|
||||
if windows::is_device_name(device_name, name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (_, v) in lock.peer_index_name.iter() {
|
||||
if windows::is_device_name(v, name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn change_resolution(index: u32, w: u32, h: u32) -> bool {
|
||||
let modes = [virtual_display::MonitorMode {
|
||||
width: w,
|
||||
height: h,
|
||||
sync: 60,
|
||||
}];
|
||||
match virtual_display::update_monitor_modes(index, &modes) {
|
||||
Ok(_) => true,
|
||||
Err(e) => {
|
||||
log::error!("Update monitor {} modes {:?} failed: {}", index, &modes, e);
|
||||
false
|
||||
for (k, v) in lock.peer_index_name.iter() {
|
||||
if windows::is_device_name(v, name) {
|
||||
return Some(change_resolution(*k, w, h));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn change_resolution_if_is_virtual_display(name: &str, w: u32, h: u32) -> Option<bool> {
|
||||
let lock = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
||||
if let Some((index, device_name)) = &lock.headless_index_name {
|
||||
if windows::is_device_name(device_name, name) {
|
||||
return Some(change_resolution(*index, w, h));
|
||||
pub mod amyuni_idd {
|
||||
use super::windows;
|
||||
use crate::platform::windows::get_amyuni_exe_name;
|
||||
use hbb_common::{bail, lazy_static, log, ResultType};
|
||||
use std::{
|
||||
ptr::null_mut,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use winapi::um::shellapi::ShellExecuteA;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref LOCK: Arc<Mutex<()>> = Default::default();
|
||||
}
|
||||
|
||||
fn run_deviceinstaller(args: &str) -> ResultType<()> {
|
||||
let Some(exe_name) = get_amyuni_exe_name() else {
|
||||
bail!("Cannot get amyuni exe name.")
|
||||
};
|
||||
|
||||
let cur_exe = std::env::current_exe()?;
|
||||
let Some(cur_dir) = cur_exe.parent() else {
|
||||
bail!("Cannot get parent of current exe file.");
|
||||
};
|
||||
|
||||
let work_dir = cur_dir.join("usbmmidd_v2");
|
||||
if !work_dir.exists() {
|
||||
bail!("usbmmidd_v2 does not exist.",);
|
||||
}
|
||||
let Some(work_dir) = work_dir.to_str() else {
|
||||
bail!("Cannot convert work_dir to string.");
|
||||
};
|
||||
let mut work_dir2 = work_dir.as_bytes().to_vec();
|
||||
work_dir2.push(0);
|
||||
|
||||
unsafe {
|
||||
const SW_HIDE: i32 = 0;
|
||||
let mut args = args.bytes().collect::<Vec<_>>();
|
||||
args.push(0);
|
||||
let mut exe_name = exe_name.bytes().collect::<Vec<_>>();
|
||||
exe_name.push(0);
|
||||
let hi = ShellExecuteA(
|
||||
null_mut(),
|
||||
"open\0".as_ptr() as _,
|
||||
exe_name.as_ptr() as _,
|
||||
args.as_ptr() as _,
|
||||
work_dir2.as_ptr() as _,
|
||||
SW_HIDE,
|
||||
) as i32;
|
||||
if hi <= 32 {
|
||||
log::error!("Failed to run deviceinstaller: {}", hi);
|
||||
bail!("Failed to run deviceinstaller.")
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
for (k, v) in lock.peer_index_name.iter() {
|
||||
if windows::is_device_name(v, name) {
|
||||
return Some(change_resolution(*k, w, h));
|
||||
fn check_install_driver() -> ResultType<()> {
|
||||
let _l = LOCK.lock().unwrap();
|
||||
let drivers = windows::get_display_drivers();
|
||||
if drivers
|
||||
.iter()
|
||||
.any(|(s, c)| s == super::AMYUNI_IDD_DEVICE_STRING && *c == 0)
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
run_deviceinstaller("install usbmmidd.inf usbmmidd")
|
||||
}
|
||||
|
||||
pub fn plug_in_headless() -> ResultType<()> {
|
||||
if get_monitor_count() > 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Err(e) = check_install_driver() {
|
||||
log::error!("Failed to install driver: {}", e);
|
||||
bail!("Failed to install driver.");
|
||||
}
|
||||
|
||||
run_deviceinstaller("enableidd 1")
|
||||
}
|
||||
|
||||
pub fn plug_in_monitor() -> ResultType<()> {
|
||||
if let Err(e) = check_install_driver() {
|
||||
log::error!("Failed to install driver: {}", e);
|
||||
bail!("Failed to install driver.");
|
||||
}
|
||||
|
||||
if get_monitor_count() == 4 {
|
||||
bail!("There are already 4 monitors plugged in.");
|
||||
}
|
||||
|
||||
run_deviceinstaller("enableidd 1")
|
||||
}
|
||||
|
||||
pub fn plug_out_monitor(index: i32) -> ResultType<()> {
|
||||
let all_count = windows::get_device_names(None).len();
|
||||
let amyuni_count = get_monitor_count();
|
||||
let mut to_plug_out_count = match all_count {
|
||||
0 => return Ok(()),
|
||||
1 => {
|
||||
if amyuni_count == 0 {
|
||||
bail!("No virtual displays to plug out.")
|
||||
} else {
|
||||
if super::is_can_plug_out_all() {
|
||||
1
|
||||
} else {
|
||||
bail!("This only virtual display cannot be pulled out.")
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if all_count == amyuni_count {
|
||||
if super::is_can_plug_out_all() {
|
||||
all_count
|
||||
} else {
|
||||
all_count - 1
|
||||
}
|
||||
} else {
|
||||
amyuni_count
|
||||
}
|
||||
}
|
||||
};
|
||||
if to_plug_out_count != 0 && index != -1 {
|
||||
to_plug_out_count = 1;
|
||||
}
|
||||
for _i in 0..to_plug_out_count {
|
||||
let _ = run_deviceinstaller(&format!("enableidd 0"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_monitor_count() -> usize {
|
||||
windows::get_device_names(Some(super::AMYUNI_IDD_DEVICE_STRING)).len()
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
mod windows {
|
||||
use std::{collections::HashSet, ptr::null_mut};
|
||||
use std::ptr::null_mut;
|
||||
use winapi::{
|
||||
shared::minwindef::{DWORD, FALSE},
|
||||
shared::{
|
||||
devguid::GUID_DEVCLASS_DISPLAY,
|
||||
minwindef::{DWORD, FALSE},
|
||||
ntdef::ULONG,
|
||||
},
|
||||
um::{
|
||||
cfgmgr32::{CM_Get_DevNode_Status, CR_SUCCESS},
|
||||
cguid::GUID_NULL,
|
||||
setupapi::{
|
||||
SetupDiEnumDeviceInfo, SetupDiGetClassDevsW, SetupDiGetDeviceRegistryPropertyW,
|
||||
SP_DEVINFO_DATA,
|
||||
},
|
||||
wingdi::{
|
||||
DEVMODEW, DISPLAY_DEVICEW, DISPLAY_DEVICE_ACTIVE, DISPLAY_DEVICE_MIRRORING_DRIVER,
|
||||
},
|
||||
winnt::HANDLE,
|
||||
winuser::{EnumDisplayDevicesW, EnumDisplaySettingsExW, ENUM_CURRENT_SETTINGS},
|
||||
},
|
||||
};
|
||||
|
||||
// This string is defined here.
|
||||
// https://github.com/fufesou/RustDeskIddDriver/blob/b370aad3f50028b039aad211df60c8051c4a64d6/RustDeskIddDriver/RustDeskIddDriver.inf#LL73C1-L73C40
|
||||
const IDD_DEVICE_STRING: &'static str = "RustDeskIddDriver Device\0";
|
||||
const DIGCF_PRESENT: DWORD = 0x00000002;
|
||||
const SPDRP_DEVICEDESC: DWORD = 0x00000000;
|
||||
const INVALID_HANDLE_VALUE: HANDLE = -1isize as HANDLE;
|
||||
|
||||
#[inline]
|
||||
pub(super) fn is_device_name(device_name: &str, name: &str) -> bool {
|
||||
@@ -281,8 +576,8 @@ mod windows {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn get_device_names() -> HashSet<String> {
|
||||
let mut device_names = HashSet::new();
|
||||
pub(super) fn get_device_names(device_string: Option<&str>) -> Vec<String> {
|
||||
let mut device_names = Vec::new();
|
||||
let mut dd: DISPLAY_DEVICEW = unsafe { std::mem::zeroed() };
|
||||
dd.cb = std::mem::size_of::<DISPLAY_DEVICEW>() as DWORD;
|
||||
let mut i_dev_num = 0;
|
||||
@@ -317,15 +612,115 @@ mod windows {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let (Ok(device_name), Ok(device_string)) = (
|
||||
if let (Ok(device_name), Ok(ds)) = (
|
||||
String::from_utf16(&dd.DeviceName),
|
||||
String::from_utf16(&dd.DeviceString),
|
||||
) {
|
||||
if &device_string[..IDD_DEVICE_STRING.len()] == IDD_DEVICE_STRING {
|
||||
device_names.insert(device_name);
|
||||
if let Some(s) = device_string {
|
||||
if ds.len() >= s.len() && &ds[..s.len()] == s {
|
||||
device_names.push(device_name);
|
||||
}
|
||||
} else {
|
||||
device_names.push(device_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
device_names
|
||||
}
|
||||
|
||||
pub(super) fn get_display_drivers() -> Vec<(String, u32)> {
|
||||
let mut display_drivers: Vec<(String, u32)> = Vec::new();
|
||||
|
||||
let device_info_set = unsafe {
|
||||
SetupDiGetClassDevsW(
|
||||
&GUID_DEVCLASS_DISPLAY,
|
||||
null_mut(),
|
||||
null_mut(),
|
||||
DIGCF_PRESENT,
|
||||
)
|
||||
};
|
||||
|
||||
if device_info_set == INVALID_HANDLE_VALUE {
|
||||
println!(
|
||||
"Failed to get device information set. Error: {}",
|
||||
std::io::Error::last_os_error()
|
||||
);
|
||||
return display_drivers;
|
||||
}
|
||||
|
||||
let mut device_info_data = SP_DEVINFO_DATA {
|
||||
cbSize: std::mem::size_of::<SP_DEVINFO_DATA>() as u32,
|
||||
ClassGuid: GUID_NULL,
|
||||
DevInst: 0,
|
||||
Reserved: 0,
|
||||
};
|
||||
|
||||
let mut device_index = 0;
|
||||
loop {
|
||||
let result = unsafe {
|
||||
SetupDiEnumDeviceInfo(device_info_set, device_index, &mut device_info_data)
|
||||
};
|
||||
if result == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
let mut data_type: DWORD = 0;
|
||||
let mut required_size: DWORD = 0;
|
||||
|
||||
// Get the required buffer size for the driver description
|
||||
let mut buffer;
|
||||
unsafe {
|
||||
SetupDiGetDeviceRegistryPropertyW(
|
||||
device_info_set,
|
||||
&mut device_info_data,
|
||||
SPDRP_DEVICEDESC,
|
||||
&mut data_type,
|
||||
null_mut(),
|
||||
0,
|
||||
&mut required_size,
|
||||
);
|
||||
|
||||
buffer = vec![0; required_size as usize / 2];
|
||||
SetupDiGetDeviceRegistryPropertyW(
|
||||
device_info_set,
|
||||
&mut device_info_data,
|
||||
SPDRP_DEVICEDESC,
|
||||
&mut data_type,
|
||||
buffer.as_mut_ptr() as *mut u8,
|
||||
required_size,
|
||||
null_mut(),
|
||||
);
|
||||
}
|
||||
|
||||
let Ok(driver_description) = String::from_utf16(&buffer) else {
|
||||
println!("Failed to convert driver description to string");
|
||||
device_index += 1;
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut status: ULONG = 0;
|
||||
let mut problem_number: ULONG = 0;
|
||||
// Get the device status and problem number
|
||||
let config_ret = unsafe {
|
||||
CM_Get_DevNode_Status(
|
||||
&mut status,
|
||||
&mut problem_number,
|
||||
device_info_data.DevInst,
|
||||
0,
|
||||
)
|
||||
};
|
||||
if config_ret != CR_SUCCESS {
|
||||
println!(
|
||||
"Failed to get device status. Error: {}",
|
||||
std::io::Error::last_os_error()
|
||||
);
|
||||
device_index += 1;
|
||||
continue;
|
||||
}
|
||||
display_drivers.push((driver_description, problem_number));
|
||||
device_index += 1;
|
||||
}
|
||||
|
||||
display_drivers
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user