diff --git a/Cargo.toml b/Cargo.toml index 0617e47..ecb69e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,3 +18,9 @@ features = [ "Win32_Graphics_Gdi", "Win32_Security", ] + +[profile.release] +lto = true +codegen-units = 1 +# panic = 'abort' +# strip = true \ No newline at end of file diff --git a/src/app.rs b/src/app.rs index c290913..3464c75 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,6 +1,7 @@ use log::{debug, error, info, trace}; use parsec_vdd::{ - open_device_handle, query_device_status, vdd_add_display, vdd_remove_display, vdd_update, vdd_version, DeviceStatus, VddHandle, VDD_ADAPTER_GUID, VDD_CLASS_GUID, VDD_HARDWARE_ID + DeviceStatus, VDD_ADAPTER_GUID, VDD_CLASS_GUID, VDD_HARDWARE_ID, VddHandle, open_device_handle, + query_device_status, vdd_add_display, vdd_remove_display, vdd_update, vdd_version, }; use std::sync::{ Arc, @@ -32,7 +33,7 @@ impl App { panic!("Failed to open device handle"); } }; - let mut app = App { + let app = App { handle: handle.clone(), version: 0, running: Arc::new(AtomicBool::new(true)), @@ -123,12 +124,11 @@ impl App { pub fn start(&mut self) { self.running.store(true, Ordering::SeqCst); - while self.running.load(Ordering::SeqCst) { - if self.index != -1 { - vdd_update(&self.handle); - } - std::thread::sleep(std::time::Duration::from_millis(100)); - } + + std::thread::spawn(move || { + self.watch_monitors(); + }); + self.keep_alive(); } pub fn keep_alive(&mut self) { @@ -137,6 +137,7 @@ impl App { std::thread::sleep(std::time::Duration::from_millis(1000)); continue; } + vdd_update(&self.handle); std::thread::sleep(std::time::Duration::from_millis(100)); } } @@ -146,10 +147,9 @@ impl App { } } - impl Drop for App { fn drop(&mut self) { self.stop(); self.stop_virtual_display(); } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 5e8f552..f70821a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ #[allow(non_snake_case)] use std::mem::{size_of, zeroed}; - +use log::{trace,debug,info}; use windows::{ Win32::{ Devices::DeviceAndDriverInstallation::{ @@ -17,6 +17,9 @@ use windows::{ CloseHandle, ERROR_IO_PENDING, GENERIC_READ, GENERIC_WRITE, GetLastError, HANDLE, INVALID_HANDLE_VALUE, }, + Graphics::Gdi::{ + DISPLAY_DEVICE_ACTIVE, DISPLAY_DEVICE_STATE_FLAGS, DISPLAY_DEVICEW, EnumDisplayDevicesW, + }, Storage::FileSystem::{ CreateFileA, FILE_ATTRIBUTE_NORMAL, FILE_FLAG_NO_BUFFERING, FILE_FLAG_OVERLAPPED, FILE_FLAG_WRITE_THROUGH, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING, @@ -27,7 +30,7 @@ use windows::{ Threading::CreateEventA, }, }, - core::{GUID, PCSTR}, + core::{GUID, PCSTR, PWSTR}, }; // RAII wrapper for HDEVINFO to ensure SetupDiDestroyDeviceInfoList is always called. @@ -57,6 +60,9 @@ impl Drop for DevInfo { // We expose this as the public handle type. pub struct VddHandle(HANDLE); +unsafe impl Send for VddHandle {} +unsafe impl Sync for VddHandle {} + impl Drop for VddHandle { fn drop(&mut self) { close_device_handle(self.0); @@ -417,3 +423,38 @@ pub fn vdd_remove_display(vdd: &VddHandle, index: i32) -> bool { } result } + +pub fn is_physical_monitor_connected() -> bool { + // fetch windows monitor lists. + let mut physical_monitors = Vec::new(); + let mut info = DISPLAY_DEVICEW { + cb: std::mem::size_of::() as u32, + DeviceName: [0; 32], + DeviceString: [0; 128], + StateFlags: DISPLAY_DEVICE_STATE_FLAGS(0), + DeviceID: [0; 128], + DeviceKey: [0; 128], + }; + + let mut i = 0; + while unsafe { EnumDisplayDevicesW(PWSTR::null(), i, &mut info, 0).as_bool() } { + if (info.StateFlags & DISPLAY_DEVICE_ACTIVE).0 != 0 { + let device_name = String::from_utf16_lossy(&info.DeviceName) + .trim_end_matches('\0') + .to_string(); + debug!("Found monitor: {}", device_name); + // 排除 parsec 虚拟显示器,只统计物理显示器 + if !device_name.to_lowercase().contains("parsec") { + physical_monitors.push(device_name); + } + } + i += 1; + } + + trace!("Found {} physical monitors", physical_monitors.len()); + for (i, monitor) in physical_monitors.iter().enumerate() { + trace!("Physical Monitor {}: {}", i, monitor); + } + + !physical_monitors.is_empty() +} diff --git a/src/main.rs b/src/main.rs index 80cc0fe..c72369e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,47 @@ -mod app; -use app::App; +use std::sync::{ + Arc, + atomic::{AtomicI32, Ordering}, +}; +use parsec_vdd::{ + DeviceStatus, VDD_ADAPTER_GUID, VDD_CLASS_GUID, VDD_HARDWARE_ID, is_physical_monitor_connected, + open_device_handle, query_device_status, vdd_add_display, vdd_remove_display, vdd_update, +}; + +static INDEX: AtomicI32 = AtomicI32::new(-1); fn main() { - let mut app = App::new(); - app.watch_monitors(); -} + let status: DeviceStatus = query_device_status(&VDD_CLASS_GUID, VDD_HARDWARE_ID); + if status != DeviceStatus::Ok { + panic!("Failed to query device status"); + } + let handle = match open_device_handle(&VDD_ADAPTER_GUID) { + Some(handle) => Arc::new(handle), + None => panic!("Failed to open device handle"), + }; + let handle_clone = Arc::clone(&handle); + std::thread::spawn(move || { + loop { + vdd_update(&handle_clone); + std::thread::sleep(std::time::Duration::from_millis(100)); + } + }); + + loop { + let vdd_is_running = INDEX.load(Ordering::SeqCst) != -1; + let has_physicial_monitor = is_physical_monitor_connected(); + if !has_physicial_monitor && !vdd_is_running { + INDEX.store( + match vdd_add_display(&handle) { + Some(i) => i, + None => -1, + }, + Ordering::SeqCst, + ); + } else if has_physicial_monitor && vdd_is_running { + vdd_remove_display(&handle, INDEX.load(Ordering::SeqCst)); + INDEX.store(-1, Ordering::SeqCst); + } + std::thread::sleep(std::time::Duration::from_millis(1000)); + } +} \ No newline at end of file