use std::fmt::Display;
use human_bytes::human_bytes;
#[derive(Debug, Clone)]
pub struct Gpu {
pub name: String,
pub version: String,
pub ram: u32,
}
impl Display for Gpu {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut v = vec![];
v.push(self.name.clone());
if self.ram > 0 {
v.push(format!("({})", human_bytes(self.ram)));
}
if !self.version.is_empty() {
v.push(format!("@ ({})", self.version));
}
f.write_str(&v.join(" "))
}
}
#[cfg(windows)]
pub async fn get_gpu() -> Option<Vec<Gpu>> {
use serde::Deserialize;
use crate::platform::wmi_query;
#[derive(Deserialize, Debug, Clone)]
#[serde(rename = "Win32_VideoController")]
pub struct VideoController {
#[serde(rename = "Caption")]
pub caption: String,
#[serde(rename = "DriverVersion")]
pub driver_version: String,
#[serde(rename = "AdapterRAM")]
pub adapter_ram: u32,
}
let results: Vec<VideoController> = wmi_query().await.ok()?;
Some(
results
.iter()
.map(|i| Gpu {
name: i.caption.to_owned(),
version: i.driver_version.to_owned(),
ram: i.adapter_ram,
})
.collect(),
)
}
#[cfg(unix)]
fn load_pci_ids() -> (
std::collections::HashMap<String, String>,
std::collections::HashMap<(String, String), String>,
) {
let content = std::fs::read_to_string("/usr/share/misc/pci.ids").unwrap_or_default();
let mut vendors = std::collections::HashMap::new();
let mut devices = std::collections::HashMap::new();
let mut current_vendor: Option<String> = None;
for line in content.lines() {
if line.is_empty() || line.starts_with('#') {
continue;
}
if !line.starts_with('\t') {
if let Some((id, name)) = line.split_once(' ') {
let id = id.trim().to_lowercase();
let name = name.trim().to_string();
current_vendor = Some(id.clone());
vendors.insert(id, name);
}
} else if let Some(vendor_id) = ¤t_vendor
&& let Some((id, name)) = line.trim().split_once(' ')
{
let id = id.trim().to_lowercase();
let name = name.trim().to_string();
devices.insert((vendor_id.clone(), id), name);
}
}
(vendors, devices)
}
#[cfg(unix)]
pub async fn get_gpu() -> Option<Vec<Gpu>> {
let path = std::path::Path::new("/sys/bus/pci/devices");
let (vendor_names, device_names) = load_pci_ids();
let mut v = vec![];
let mut dir = tokio::fs::read_dir(path).await.ok()?;
while let Some(entry) = dir.next_entry().await.ok()? {
let device_path = entry.path();
let vendor = tokio::fs::read_to_string(device_path.join("vendor"))
.await
.unwrap_or_default();
let device = tokio::fs::read_to_string(device_path.join("device"))
.await
.unwrap_or_default();
let vendor_id = vendor.trim_start_matches("0x").trim().to_lowercase();
let device_id = device.trim_start_matches("0x").trim().to_lowercase();
if let (Some(vendor_name), Some(device_name)) = (
vendor_names.get(&vendor_id),
device_names.get(&(vendor_id.clone(), device_id.clone())),
) && ["Display", "3D", "VGA"]
.into_iter()
.any(|i| device_name.as_str().contains(i))
{
v.push(Gpu {
name: vendor_name.to_owned(),
version: device_name.to_owned(),
ram: 0,
});
}
}
Some(v)
}