Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows: Implement gpus #149

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,12 @@ wmi = "0.12.0"
winreg = "0.10.1"
windows = { version = "0.39.0", features = [
"Win32_Foundation",
"Win32_Graphics_Gdi",
"Win32_System_Power",
"Win32_System_SystemInformation",
"Win32_System_WindowsProgramming"
"Win32_System_WindowsProgramming",
"Win32_UI_HiDpi",
"Win32_UI_WindowsAndMessaging",
]}

[target.'cfg(not(target_os = "windows"))'.dependencies]
Expand Down
129 changes: 126 additions & 3 deletions src/windows/mod.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
use crate::traits::*;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
use winreg::enums::*;
use winreg::RegKey;
use wmi::WMIResult;
use wmi::{COMLibrary, Variant, WMIConnection};

use windows::{
core::PSTR, Win32::System::Power::GetSystemPowerStatus,
core::{PSTR, PCWSTR},
Win32::Graphics::Gdi::{
EnumDisplayDevicesW,
DISPLAY_DEVICEW,
},
Win32::System::Power::GetSystemPowerStatus,
Win32::System::Power::SYSTEM_POWER_STATUS,
Win32::System::SystemInformation::GetComputerNameExA,
Win32::System::SystemInformation::GetTickCount64,
Win32::System::SystemInformation::GlobalMemoryStatusEx,
Win32::System::SystemInformation::MEMORYSTATUSEX,
Win32::System::WindowsProgramming::GetUserNameA,
Win32::UI::WindowsAndMessaging::EDD_GET_DEVICE_INTERFACE_NAME,
};

impl From<wmi::WMIError> for ReadoutError {
Expand Down Expand Up @@ -343,7 +349,124 @@ impl GeneralReadout for WindowsGeneralReadout {
}

fn gpus(&self) -> Result<Vec<String>, ReadoutError> {
Err(ReadoutError::NotImplemented)
// Sources:
// https://github.com/Carterpersall/OxiFetch/blob/main/src/main.rs#L360
// https://github.com/lptstr/winfetch/pull/155

// Create the Vector to store each GPU's name.
let mut output: Vec<String> = Vec::new();

let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);

// Open the location where some DirectX information is stored
if let Ok(dx_key) = hklm.open_subkey("SOFTWARE\\Microsoft\\DirectX\\") {
// Get the parent key's LastSeen value
if let Ok(lastseen) = dx_key.get_value::<u64, _>("LastSeen") {
// Iterate over the parent key's subkeys and find the ones with the same LastSeen value
for key in dx_key.enum_keys() {
if key.is_err() {
continue;
}
let key = key.unwrap();

let sublastseen = match dx_key
.open_subkey(&key)
.unwrap()
Carterpersall marked this conversation as resolved.
Show resolved Hide resolved
.get_value::<u64, _>("LastSeen")
{
Ok(key) => key,
Err(_) => continue,
};

if sublastseen == lastseen {
// Get the GPU's name
let name = match dx_key
.open_subkey(&key)
.unwrap()
.get_value::<String, _>("Description")
{
Ok(key) => key,
Err(_) => continue,
};

// Exclude the Microsoft Basic Render Driver
if name == "Microsoft Basic Render Driver" {
continue;
}

// Add the GPU's name to the output vector
output.push(name);
}
}
};
};

// Some systems have a DirectX key that lacks the LastSeen value, so a backup method is needed.
if !output.is_empty() {
return Ok(output);
}

// Alternative Method 1: Get GPUs by getting every display device
let mut devices = Vec::new();
let mut index = 0;
let mut status = true;
// Iterate over EnumDisplayDevicesW until it returns false
while status {
devices.push(DISPLAY_DEVICEW::default());
devices[index].cb = std::mem::size_of::<DISPLAY_DEVICEW>() as u32;
unsafe {
status = EnumDisplayDevicesW(
PCWSTR::null(),
index as u32,
&mut devices[index],
EDD_GET_DEVICE_INTERFACE_NAME,
).as_bool();
};
index += 1;
}
// Remove the last element, which will be invalid
devices.pop();

// Create a hashset to store the GPU names, which will prevent duplicates
let mut gpus: HashSet<String> = HashSet::new();

// Iterate over each device
for device in devices {
// Convert [u16; 128] to a String and add to the HashSet
match String::from_utf16(&device.DeviceString) {
Ok(gpu) => {
gpus.insert(gpu.trim_matches(char::from(0)).to_string());
}
Err(_) => continue,
}
}

if !gpus.is_empty() {
// Convert the HashSet to a Vec and return it
return Ok(gpus.into_iter().collect());
}

// Alternative Method 2: Use WMI to query Win32_VideoController

// Create a WMI connection
let wmi_con = wmi_connection()?;

// Query the WMI connection
let results: Vec<HashMap<String, Variant>> =
wmi_con.raw_query("SELECT Name FROM Win32_VideoController")?;

// Get each GPU's name
for result in results {
if let Some(Variant::String(gpu)) = result.get("Name") {
output.push(gpu.to_string());
}
}

if !output.is_empty() {
return Ok(output);
}

Err(ReadoutError::Other("Failed to find any GPUs.".to_string()))
}
}

Expand Down