114 lines
3.9 KiB
Rust
114 lines
3.9 KiB
Rust
#[allow(non_snake_case)]
|
|
|
|
use parsec_vdd::{
|
|
query_device_status, open_device_handle, vdd_version, vdd_update,
|
|
vdd_add_display, vdd_remove_display, DeviceStatus, VddHandle,
|
|
VDD_CLASS_GUID, VDD_HARDWARE_ID, VDD_ADAPTER_GUID, VDD_MAX_DISPLAYS
|
|
};
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
use std::sync::Arc;
|
|
use std::thread;
|
|
use std::time::Duration;
|
|
|
|
// FFI import for _getch() to read a single character without waiting for Enter.
|
|
#[link(name = "msvcrt")]
|
|
extern "C" {
|
|
fn _getch() -> i32;
|
|
}
|
|
|
|
// Main application logic, translated from main.c
|
|
fn main() {
|
|
// 1. Check driver status.
|
|
let status = query_device_status(&VDD_CLASS_GUID, VDD_HARDWARE_ID);
|
|
|
|
if status != DeviceStatus::Ok {
|
|
println!("Parsec VDD device is not OK, status: {:?}", status);
|
|
return;
|
|
}
|
|
println!("Parsec VDD device is OK.");
|
|
|
|
// 2. Obtain device handle.
|
|
// The handle is wrapped in an Option and our RAII VddHandle struct.
|
|
let vdd = match open_device_handle(&VDD_ADAPTER_GUID) {
|
|
Some(handle) => Arc::new(handle), // Use Arc for sharing between threads
|
|
None => {
|
|
println!("Failed to obtain the device handle.");
|
|
return;
|
|
}
|
|
};
|
|
println!("Successfully obtained device handle.");
|
|
if let Some(version) = vdd_version(&vdd) {
|
|
println!("Driver version: {}", version);
|
|
}
|
|
|
|
// 3. Set up the updater thread.
|
|
let running = Arc::new(AtomicBool::new(true));
|
|
let running_clone = Arc::clone(&running);
|
|
let vdd_clone = Arc::clone(&vdd);
|
|
|
|
let updater_thread = thread::spawn(move || {
|
|
while running_clone.load(Ordering::SeqCst) {
|
|
vdd_update(&vdd_clone);
|
|
thread::sleep(Duration::from_millis(100));
|
|
}
|
|
});
|
|
|
|
// 4. Main interaction loop.
|
|
let mut displays: Vec<i32> = Vec::new();
|
|
println!("\nPress 'a' to add a virtual display.");
|
|
println!("Press 'r' to remove the last added display.");
|
|
println!("Press 'q' to quit (this will unplug all displays).");
|
|
|
|
while running.load(Ordering::SeqCst) {
|
|
// Use the imported _getch function.
|
|
let key = unsafe { _getch() as u8 as char };
|
|
|
|
match key {
|
|
'q' | 'Q' => {
|
|
println!("\nQuitting...");
|
|
running.store(false, Ordering::SeqCst);
|
|
}
|
|
'a' | 'A' => {
|
|
if displays.len() < VDD_MAX_DISPLAYS {
|
|
if let Some(index) = vdd_add_display(&vdd) {
|
|
if index != -1 {
|
|
displays.push(index);
|
|
println!("Added a new virtual display, index: {}", index);
|
|
} else {
|
|
println!("Failed to add virtual display (driver returned -1).");
|
|
}
|
|
} else {
|
|
println!("IOCTL call to add display failed.");
|
|
}
|
|
} else {
|
|
println!(
|
|
"Limit exceeded ({}), could not add more virtual displays.",
|
|
VDD_MAX_DISPLAYS
|
|
);
|
|
}
|
|
}
|
|
'r' | 'R' => {
|
|
if let Some(index) = displays.pop() {
|
|
vdd_remove_display(&vdd, index);
|
|
println!("Removed the last virtual display, index: {}", index);
|
|
} else {
|
|
println!("No added virtual displays to remove.");
|
|
}
|
|
}
|
|
_ => {} // Ignore other keys
|
|
}
|
|
}
|
|
|
|
// 5. Cleanup.
|
|
println!("Removing all displays before exiting...");
|
|
for index in displays {
|
|
vdd_remove_display(&vdd, index);
|
|
}
|
|
|
|
// Wait for the updater thread to finish.
|
|
updater_thread.join().expect("Updater thread panicked");
|
|
|
|
println!("Cleanup complete. Exiting.");
|
|
// The VddHandle (wrapped in Arc) will be dropped automatically here,
|
|
// which calls CloseHandle, cleaning up the resource.
|
|
} |