#[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 = 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. }