feat: add hardware detection and monitoring via WMI
parent
4269ddb312
commit
c7b4c9d33a
|
@ -7,6 +7,9 @@ edition = "2024"
|
|||
log = "0.4.27"
|
||||
env_logger = "0.11"
|
||||
thiserror = "1.0"
|
||||
wmi = "0.13"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
|
||||
[dependencies.windows]
|
||||
version = "0.61.3"
|
||||
|
|
|
@ -1,72 +1,117 @@
|
|||
use crate::display::{DisplayManager, VirtualDisplayController};
|
||||
use crate::device::{VddHandle, DeviceController, DeviceStatusChecker};
|
||||
use crate::monitor::HardwareDetector;
|
||||
use crate::config::{Config, VDD_ADAPTER_GUID, VDD_CLASS_GUID, VDD_HARDWARE_ID};
|
||||
use crate::error::Result;
|
||||
use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
|
||||
use std::time::Duration;
|
||||
use log::error;
|
||||
use log::{error, info, warn};
|
||||
|
||||
pub struct VddService {
|
||||
handle: Arc<VddHandle>,
|
||||
device_controller: DeviceController,
|
||||
display_manager: DisplayManager,
|
||||
hardware_detector: Option<HardwareDetector>,
|
||||
config: Config,
|
||||
running: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl VddService {
|
||||
pub fn new() -> Result<Self> {
|
||||
DeviceStatusChecker::check_driver_status(&VDD_CLASS_GUID, VDD_HARDWARE_ID)?;
|
||||
info!("Initializing VDD Service...");
|
||||
|
||||
// Check driver status first
|
||||
DeviceStatusChecker::check_driver_status(&VDD_CLASS_GUID, VDD_HARDWARE_ID)?;
|
||||
info!("VDD driver status: OK");
|
||||
|
||||
// Open device handle
|
||||
let handle = Arc::new(VddHandle::open(&VDD_ADAPTER_GUID)?);
|
||||
info!("VDD device handle opened successfully");
|
||||
|
||||
let config = Config::default();
|
||||
let device_controller = DeviceController::new(config.clone());
|
||||
let virtual_display_controller = VirtualDisplayController::new(device_controller.clone());
|
||||
let display_manager = DisplayManager::new(virtual_display_controller);
|
||||
|
||||
// Initialize hardware detector
|
||||
let hardware_detector = match HardwareDetector::new() {
|
||||
Ok(detector) => {
|
||||
info!("Hardware detector initialized successfully");
|
||||
if let Err(e) = detector.log_hardware_summary() {
|
||||
warn!("Failed to log hardware summary: {}", e);
|
||||
}
|
||||
Some(detector)
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Failed to initialize hardware detector: {}", e);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
handle,
|
||||
device_controller,
|
||||
display_manager,
|
||||
hardware_detector,
|
||||
config,
|
||||
running: Arc::new(AtomicBool::new(false)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn start(&mut self) -> Result<()> {
|
||||
info!("Starting VDD Service...");
|
||||
self.running.store(true, Ordering::SeqCst);
|
||||
|
||||
// Create display manager for the monitor thread
|
||||
let virtual_display_controller = VirtualDisplayController::new(self.device_controller.clone());
|
||||
let mut display_manager = DisplayManager::new(virtual_display_controller);
|
||||
|
||||
// Start monitor watching thread
|
||||
let handle_clone = Arc::clone(&self.handle);
|
||||
let running_clone = Arc::clone(&self.running);
|
||||
let config = self.config.clone();
|
||||
let mut display_manager = std::mem::replace(&mut self.display_manager,
|
||||
DisplayManager::new(VirtualDisplayController::new(self.device_controller.clone())));
|
||||
let monitor_config = self.config.clone();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
info!("Monitor detection thread started");
|
||||
|
||||
// Initial delay to let system stabilize
|
||||
std::thread::sleep(Duration::from_millis(
|
||||
monitor_config.display.virtual_display_settings.startup_delay_ms as u64
|
||||
));
|
||||
|
||||
while running_clone.load(Ordering::SeqCst) {
|
||||
if let Err(e) = display_manager.update_displays(&handle_clone) {
|
||||
error!("Failed to update displays: {}", e);
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(config.monitor_check_interval_ms));
|
||||
std::thread::sleep(Duration::from_millis(monitor_config.monitor_check_interval_ms));
|
||||
}
|
||||
|
||||
info!("Monitor detection thread stopped");
|
||||
});
|
||||
|
||||
// Start update loop
|
||||
info!("VDD Service started successfully");
|
||||
|
||||
// Start update loop in main thread
|
||||
self.update_loop()
|
||||
}
|
||||
|
||||
pub fn stop(&self) {
|
||||
info!("Stopping VDD Service...");
|
||||
self.running.store(false, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
pub fn get_hardware_info(&self) -> Option<crate::monitor::HardwareInfo> {
|
||||
self.hardware_detector.as_ref()
|
||||
.and_then(|detector| detector.get_hardware_info().ok())
|
||||
}
|
||||
|
||||
fn update_loop(&self) -> Result<()> {
|
||||
info!("VDD update loop started");
|
||||
|
||||
while self.running.load(Ordering::SeqCst) {
|
||||
if let Err(e) = self.device_controller.update(&self.handle) {
|
||||
error!("Failed to update VDD: {}", e);
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(self.config.vdd_update_interval_ms));
|
||||
}
|
||||
|
||||
info!("VDD update loop stopped");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -74,5 +119,6 @@ impl VddService {
|
|||
impl Drop for VddService {
|
||||
fn drop(&mut self) {
|
||||
self.stop();
|
||||
info!("VDD Service dropped");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DisplayConfig {
|
||||
pub auto_manage_virtual_display: bool,
|
||||
pub prefer_wmi_detection: bool,
|
||||
pub fallback_to_gdi: bool,
|
||||
pub monitor_check_interval_ms: u64,
|
||||
pub hardware_detection_timeout_ms: u32,
|
||||
pub virtual_display_settings: VirtualDisplaySettings,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct VirtualDisplaySettings {
|
||||
pub default_width: u32,
|
||||
pub default_height: u32,
|
||||
pub default_refresh_rate: u32,
|
||||
pub auto_remove_on_physical_connect: bool,
|
||||
pub startup_delay_ms: u32,
|
||||
}
|
||||
|
||||
impl Default for DisplayConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
auto_manage_virtual_display: true,
|
||||
prefer_wmi_detection: true,
|
||||
fallback_to_gdi: true,
|
||||
monitor_check_interval_ms: 1000,
|
||||
hardware_detection_timeout_ms: 5000,
|
||||
virtual_display_settings: VirtualDisplaySettings::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for VirtualDisplaySettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
default_width: 1920,
|
||||
default_height: 1080,
|
||||
default_refresh_rate: 60,
|
||||
auto_remove_on_physical_connect: true,
|
||||
startup_delay_ms: 1000,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,8 @@
|
|||
use windows::core::GUID;
|
||||
|
||||
mod display;
|
||||
pub use display::{DisplayConfig, VirtualDisplaySettings};
|
||||
|
||||
// Constants from the C header file
|
||||
pub const VDD_DISPLAY_ID: &[u8] = b"PSCCDD0\0";
|
||||
pub const VDD_DISPLAY_NAME: &[u8] = b"ParsecVDA\0";
|
||||
|
@ -27,6 +30,7 @@ pub struct Config {
|
|||
pub monitor_check_interval_ms: u64,
|
||||
pub vdd_update_interval_ms: u64,
|
||||
pub io_timeout_ms: u32,
|
||||
pub display: DisplayConfig,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
|
@ -35,6 +39,7 @@ impl Default for Config {
|
|||
monitor_check_interval_ms: 1000,
|
||||
vdd_update_interval_ms: 100,
|
||||
io_timeout_ms: 5000,
|
||||
display: DisplayConfig::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
40
src/main.rs
40
src/main.rs
|
@ -1,20 +1,52 @@
|
|||
use parsec_vdd::app::VddService;
|
||||
use log::error;
|
||||
use log::{error, info};
|
||||
use env_logger::Env;
|
||||
use std::io::{self, Write};
|
||||
|
||||
fn main() {
|
||||
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
|
||||
// Initialize logging with timestamp
|
||||
env_logger::Builder::from_env(Env::default().default_filter_or("info"))
|
||||
.format(|buf, record| {
|
||||
writeln!(buf, "{} [{}] - {}",
|
||||
chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
|
||||
record.level(),
|
||||
record.args()
|
||||
)
|
||||
})
|
||||
.init();
|
||||
|
||||
info!("=== Parsec VDD Service Starting ===");
|
||||
|
||||
let mut service = match VddService::new() {
|
||||
Ok(service) => service,
|
||||
Ok(service) => {
|
||||
info!("VDD Service initialized successfully");
|
||||
service
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to initialize VDD service: {}", e);
|
||||
error!("Please ensure:");
|
||||
error!(" 1. Parsec VDD driver is installed");
|
||||
error!(" 2. Running with administrator privileges");
|
||||
error!(" 3. Windows version is supported");
|
||||
|
||||
println!("Press Enter to exit...");
|
||||
let _ = io::stdin().read_line(&mut String::new());
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
// Log hardware information if available
|
||||
if let Some(hardware) = service.get_hardware_info() {
|
||||
info!("Detected {} video controllers and {} monitors",
|
||||
hardware.video_controllers.len(),
|
||||
hardware.monitors.len());
|
||||
}
|
||||
|
||||
info!("Starting service main loop...");
|
||||
if let Err(e) = service.start() {
|
||||
error!("Failed to start VDD service: {}", e);
|
||||
error!("VDD service encountered an error: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
info!("VDD Service stopped normally");
|
||||
}
|
||||
|
|
|
@ -6,6 +6,14 @@ use windows::{
|
|||
core::PWSTR,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DisplayInfo {
|
||||
pub device_name: String,
|
||||
pub device_string: String,
|
||||
pub is_active: bool,
|
||||
pub is_parsec_virtual: bool,
|
||||
}
|
||||
|
||||
pub struct MonitorDetector;
|
||||
|
||||
impl MonitorDetector {
|
||||
|
@ -47,4 +55,39 @@ impl MonitorDetector {
|
|||
|
||||
physical_monitors
|
||||
}
|
||||
|
||||
pub fn get_all_displays() -> Vec<DisplayInfo> {
|
||||
let mut displays = Vec::new();
|
||||
let mut info = DISPLAY_DEVICEW {
|
||||
cb: std::mem::size_of::<DISPLAY_DEVICEW>() 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() } {
|
||||
let device_name = String::from_utf16_lossy(&info.DeviceName)
|
||||
.trim_end_matches('\0')
|
||||
.to_string();
|
||||
let device_string = String::from_utf16_lossy(&info.DeviceString)
|
||||
.trim_end_matches('\0')
|
||||
.to_string();
|
||||
let is_active = (info.StateFlags & DISPLAY_DEVICE_ACTIVE).0 != 0;
|
||||
let is_parsec_virtual = device_name.to_lowercase().contains("parsec");
|
||||
|
||||
displays.push(DisplayInfo {
|
||||
device_name,
|
||||
device_string,
|
||||
is_active,
|
||||
is_parsec_virtual,
|
||||
});
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
displays
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,186 @@
|
|||
use crate::monitor::wmi::{WmiDeviceManager, VideoController, DesktopMonitor};
|
||||
use crate::error::Result;
|
||||
use log::{debug, info, warn};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HardwareInfo {
|
||||
pub video_controllers: Vec<VideoControllerInfo>,
|
||||
pub monitors: Vec<MonitorInfo>,
|
||||
pub total_vram: u64,
|
||||
pub has_dedicated_gpu: bool,
|
||||
pub primary_adapter: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VideoControllerInfo {
|
||||
pub name: String,
|
||||
pub device_id: String,
|
||||
pub pnp_device_id: String,
|
||||
pub driver_version: Option<String>,
|
||||
pub adapter_ram: u64,
|
||||
pub status: String,
|
||||
pub is_primary: bool,
|
||||
pub vendor: GpuVendor,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MonitorInfo {
|
||||
pub name: String,
|
||||
pub device_id: String,
|
||||
pub manufacturer: Option<String>,
|
||||
pub width: Option<u32>,
|
||||
pub height: Option<u32>,
|
||||
pub is_physical: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum GpuVendor {
|
||||
Nvidia,
|
||||
Amd,
|
||||
Intel,
|
||||
Microsoft, // Basic Display Adapter
|
||||
Unknown,
|
||||
}
|
||||
|
||||
pub struct HardwareDetector {
|
||||
wmi_manager: WmiDeviceManager,
|
||||
}
|
||||
|
||||
impl HardwareDetector {
|
||||
pub fn new() -> Result<Self> {
|
||||
let wmi_manager = WmiDeviceManager::new()?;
|
||||
Ok(Self { wmi_manager })
|
||||
}
|
||||
|
||||
pub fn get_hardware_info(&self) -> Result<HardwareInfo> {
|
||||
let video_controllers = self.get_video_controller_info()?;
|
||||
let monitors = self.get_monitor_info()?;
|
||||
|
||||
let total_vram = video_controllers.iter()
|
||||
.map(|vc| vc.adapter_ram)
|
||||
.sum();
|
||||
|
||||
let has_dedicated_gpu = video_controllers.iter()
|
||||
.any(|vc| vc.vendor != GpuVendor::Microsoft && vc.adapter_ram > 0);
|
||||
|
||||
let primary_adapter = video_controllers.iter()
|
||||
.find(|vc| vc.is_primary)
|
||||
.map(|vc| vc.name.clone());
|
||||
|
||||
Ok(HardwareInfo {
|
||||
video_controllers,
|
||||
monitors,
|
||||
total_vram,
|
||||
has_dedicated_gpu,
|
||||
primary_adapter,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_video_controller_info(&self) -> Result<Vec<VideoControllerInfo>> {
|
||||
let controllers = self.wmi_manager.get_video_controllers()?;
|
||||
let mut controller_info = Vec::new();
|
||||
|
||||
for (index, controller) in controllers.into_iter().enumerate() {
|
||||
if let (Some(name), Some(device_id)) = (&controller.name, &controller.device_id) {
|
||||
let vendor = Self::detect_gpu_vendor(name);
|
||||
let is_primary = index == 0; // First controller is typically primary
|
||||
|
||||
controller_info.push(VideoControllerInfo {
|
||||
name: name.clone(),
|
||||
device_id: device_id.clone(),
|
||||
pnp_device_id: controller.pnp_device_id.unwrap_or_default(),
|
||||
driver_version: controller.driver_version,
|
||||
adapter_ram: controller.adapter_ram.unwrap_or(0),
|
||||
status: controller.status.unwrap_or_else(|| "Unknown".to_string()),
|
||||
is_primary,
|
||||
vendor,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
debug!("Found {} video controllers", controller_info.len());
|
||||
Ok(controller_info)
|
||||
}
|
||||
|
||||
fn get_monitor_info(&self) -> Result<Vec<MonitorInfo>> {
|
||||
let monitors = self.wmi_manager.get_desktop_monitors()?;
|
||||
let mut monitor_info = Vec::new();
|
||||
|
||||
for monitor in monitors {
|
||||
if let (Some(name), Some(device_id)) = (&monitor.name, &monitor.device_id) {
|
||||
let is_physical = Self::is_physical_monitor(name);
|
||||
|
||||
monitor_info.push(MonitorInfo {
|
||||
name: name.clone(),
|
||||
device_id: device_id.clone(),
|
||||
manufacturer: monitor.manufacturer,
|
||||
width: monitor.screen_width,
|
||||
height: monitor.screen_height,
|
||||
is_physical,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
debug!("Found {} monitors", monitor_info.len());
|
||||
Ok(monitor_info)
|
||||
}
|
||||
|
||||
fn detect_gpu_vendor(name: &str) -> GpuVendor {
|
||||
let name_lower = name.to_lowercase();
|
||||
|
||||
if name_lower.contains("nvidia") || name_lower.contains("geforce") || name_lower.contains("quadro") {
|
||||
GpuVendor::Nvidia
|
||||
} else if name_lower.contains("amd") || name_lower.contains("radeon") || name_lower.contains("ati") {
|
||||
GpuVendor::Amd
|
||||
} else if name_lower.contains("intel") {
|
||||
GpuVendor::Intel
|
||||
} else if name_lower.contains("microsoft") || name_lower.contains("basic display") {
|
||||
GpuVendor::Microsoft
|
||||
} else {
|
||||
GpuVendor::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
fn is_physical_monitor(name: &str) -> bool {
|
||||
let name_lower = name.to_lowercase();
|
||||
!name_lower.contains("parsec") &&
|
||||
!name_lower.contains("virtual") &&
|
||||
!name_lower.contains("generic pnp") &&
|
||||
!name_lower.contains("software")
|
||||
}
|
||||
|
||||
pub fn log_hardware_summary(&self) -> Result<()> {
|
||||
let hardware = self.get_hardware_info()?;
|
||||
|
||||
info!("=== Hardware Summary ===");
|
||||
info!("Video Controllers: {}", hardware.video_controllers.len());
|
||||
info!("Total VRAM: {} MB", hardware.total_vram / (1024 * 1024));
|
||||
info!("Has Dedicated GPU: {}", hardware.has_dedicated_gpu);
|
||||
|
||||
if let Some(primary) = &hardware.primary_adapter {
|
||||
info!("Primary Adapter: {}", primary);
|
||||
}
|
||||
|
||||
for (i, controller) in hardware.video_controllers.iter().enumerate() {
|
||||
info!(" GPU {}: {} ({:?}) - {} MB VRAM",
|
||||
i, controller.name, controller.vendor,
|
||||
controller.adapter_ram / (1024 * 1024));
|
||||
}
|
||||
|
||||
let physical_monitors: Vec<_> = hardware.monitors.iter()
|
||||
.filter(|m| m.is_physical)
|
||||
.collect();
|
||||
|
||||
info!("Physical Monitors: {}", physical_monitors.len());
|
||||
for (i, monitor) in physical_monitors.iter().enumerate() {
|
||||
let resolution = match (monitor.width, monitor.height) {
|
||||
(Some(w), Some(h)) => format!("{}x{}", w, h),
|
||||
_ => "Unknown".to_string(),
|
||||
};
|
||||
info!(" Monitor {}: {} ({})", i, monitor.name, resolution);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,3 +1,7 @@
|
|||
mod detector;
|
||||
mod wmi;
|
||||
mod hardware;
|
||||
|
||||
pub use detector::MonitorDetector;
|
||||
pub use detector::{MonitorDetector, DisplayInfo};
|
||||
pub use wmi::{WmiDeviceManager, VideoController, DesktopMonitor};
|
||||
pub use hardware::{HardwareDetector, HardwareInfo, VideoControllerInfo, MonitorInfo, GpuVendor};
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
use crate::error::{Result, VddError};
|
||||
use log::{debug, error, warn};
|
||||
use std::time::Duration;
|
||||
use wmi::{COMLibrary, WMIConnection, Variant};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VideoController {
|
||||
pub name: Option<String>,
|
||||
pub device_id: Option<String>,
|
||||
pub pnp_device_id: Option<String>,
|
||||
pub driver_version: Option<String>,
|
||||
pub adapter_ram: Option<u64>,
|
||||
pub status: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DesktopMonitor {
|
||||
pub name: Option<String>,
|
||||
pub device_id: Option<String>,
|
||||
pub manufacturer: Option<String>,
|
||||
pub screen_width: Option<u32>,
|
||||
pub screen_height: Option<u32>,
|
||||
}
|
||||
|
||||
pub struct WmiDeviceManager {
|
||||
wmi_con: WMIConnection,
|
||||
}
|
||||
|
||||
impl WmiDeviceManager {
|
||||
pub fn new() -> Result<Self> {
|
||||
let com_lib = COMLibrary::new().map_err(|e| {
|
||||
error!("Failed to initialize COM library: {}", e);
|
||||
VddError::IoError
|
||||
})?;
|
||||
|
||||
let wmi_con = WMIConnection::new(com_lib).map_err(|e| {
|
||||
error!("Failed to connect to WMI: {}", e);
|
||||
VddError::IoError
|
||||
})?;
|
||||
|
||||
Ok(Self { wmi_con })
|
||||
}
|
||||
|
||||
pub fn get_video_controllers(&self) -> Result<Vec<VideoController>> {
|
||||
let results: Vec<HashMap<String, Variant>> = self.wmi_con
|
||||
.raw_query("SELECT Name, DeviceID, PNPDeviceID, DriverVersion, AdapterRAM, Status FROM Win32_VideoController")
|
||||
.map_err(|e| {
|
||||
error!("WMI query for video controllers failed: {}", e);
|
||||
VddError::IoError
|
||||
})?;
|
||||
|
||||
let mut controllers = Vec::new();
|
||||
for result in results {
|
||||
controllers.push(VideoController {
|
||||
name: Self::extract_string(&result, "Name"),
|
||||
device_id: Self::extract_string(&result, "DeviceID"),
|
||||
pnp_device_id: Self::extract_string(&result, "PNPDeviceID"),
|
||||
driver_version: Self::extract_string(&result, "DriverVersion"),
|
||||
adapter_ram: Self::extract_u64(&result, "AdapterRAM"),
|
||||
status: Self::extract_string(&result, "Status"),
|
||||
});
|
||||
}
|
||||
|
||||
debug!("Found {} video controllers via WMI", controllers.len());
|
||||
Ok(controllers)
|
||||
}
|
||||
|
||||
pub fn get_desktop_monitors(&self) -> Result<Vec<DesktopMonitor>> {
|
||||
let results: Vec<HashMap<String, Variant>> = self.wmi_con
|
||||
.raw_query("SELECT Name, DeviceID, MonitorManufacturer, ScreenWidth, ScreenHeight FROM Win32_DesktopMonitor")
|
||||
.map_err(|e| {
|
||||
error!("WMI query for desktop monitors failed: {}", e);
|
||||
VddError::IoError
|
||||
})?;
|
||||
|
||||
let mut monitors = Vec::new();
|
||||
for result in results {
|
||||
monitors.push(DesktopMonitor {
|
||||
name: Self::extract_string(&result, "Name"),
|
||||
device_id: Self::extract_string(&result, "DeviceID"),
|
||||
manufacturer: Self::extract_string(&result, "MonitorManufacturer"),
|
||||
screen_width: Self::extract_u32(&result, "ScreenWidth"),
|
||||
screen_height: Self::extract_u32(&result, "ScreenHeight"),
|
||||
});
|
||||
}
|
||||
|
||||
debug!("Found {} desktop monitors via WMI", monitors.len());
|
||||
Ok(monitors)
|
||||
}
|
||||
|
||||
fn extract_string(map: &HashMap<String, Variant>, key: &str) -> Option<String> {
|
||||
match map.get(key) {
|
||||
Some(Variant::String(s)) => Some(s.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_u32(map: &HashMap<String, Variant>, key: &str) -> Option<u32> {
|
||||
match map.get(key) {
|
||||
Some(Variant::UI4(n)) => Some(*n),
|
||||
Some(Variant::I4(n)) => Some(*n as u32),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_u64(map: &HashMap<String, Variant>, key: &str) -> Option<u64> {
|
||||
match map.get(key) {
|
||||
Some(Variant::UI8(n)) => Some(*n),
|
||||
Some(Variant::I8(n)) => Some(*n as u64),
|
||||
Some(Variant::UI4(n)) => Some(*n as u64),
|
||||
Some(Variant::I4(n)) => Some(*n as u64),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue