use std::ptr::null_mut;
use winapi::shared::minwindef::DWORD;
use winapi::shared::winerror::ERROR_SUCCESS;
use winapi::um::sysinfoapi::{GetSystemInfo, SYSTEM_INFO};
use winapi::um::winreg::{HKEY_LOCAL_MACHINE, RRF_RT_REG_DWORD, RRF_RT_REG_SZ, RegGetValueW};
pub fn get_cpu(
cpu_brand: bool,
show_speed: bool,
show_cores: bool,
show_temp: bool,
speed_shorthand: bool,
temp_unit: Option<char>,
) -> Option<String> {
let mut output = get_cpu_model(cpu_brand);
if show_cores {
let cores = get_core_count();
output = format!("{} ({})", output, cores);
}
if show_speed {
if let Some(mhz) = get_cpu_speed_mhz() {
let formatted = if mhz < 1000 {
format!("{}MHz", mhz)
} else {
let mut ghz = mhz as f32 / 1000.0;
if speed_shorthand {
ghz = (ghz * 10.0).round() / 10.0;
}
format!("{:.1}GHz", ghz)
};
output = format!("{} @ {}", output, formatted);
}
}
if show_temp {
if let Some(mut celsius) = get_cpu_temperature() {
if let Some('F') = temp_unit {
celsius = celsius * 9.0 / 5.0 + 32.0;
}
output = format!("{} [{:.1}°{}]", output, celsius, temp_unit.unwrap_or('C'));
}
}
Some(output)
}
fn get_cpu_model(show_brand: bool) -> String {
if let Some(name) = read_reg_sz(
HKEY_LOCAL_MACHINE,
"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
"ProcessorNameString",
) {
return sanitize_cpu_model(&name, show_brand);
}
"Unknown CPU".to_string()
}
fn get_core_count() -> u32 {
unsafe {
let mut info: SYSTEM_INFO = std::mem::zeroed();
GetSystemInfo(&mut info as *mut _);
let n = info.dwNumberOfProcessors;
if n == 0 { 1 } else { n }
}
}
fn get_cpu_speed_mhz() -> Option<u32> {
let key = "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
let name = "~MHz";
let mut data: DWORD = 0;
let mut data_size = std::mem::size_of::<DWORD>() as u32;
let status = unsafe {
RegGetValueW(
HKEY_LOCAL_MACHINE,
to_wide(key).as_ptr(),
to_wide(name).as_ptr(),
RRF_RT_REG_DWORD,
null_mut(),
&mut data as *mut _ as *mut _,
&mut data_size,
)
};
if status == ERROR_SUCCESS as i32 {
Some(data as u32)
} else {
None
}
}
fn get_cpu_temperature() -> Option<f32> {
None
}
fn sanitize_cpu_model(model: &str, show_brand: bool) -> String {
let mut s = model.to_string();
let remove_patterns = [
"(TM)",
"(tm)",
"(R)",
"(r)",
"CPU",
"Processor",
"with Radeon",
"Technologies, Inc",
"Core2",
"Chip Revision",
"Compute Cores",
"FPU",
];
for pat in remove_patterns.iter() {
s = s.replace(pat, "");
}
if !show_brand {
let brands = ["AMD ", "Intel ", "Qualcomm ", "Apple "];
for b in brands {
s = s.replacen(b, "", 1);
}
}
s = s
.split_whitespace()
.filter(|word| {
if let Some(stripped) = word.strip_suffix("-Core") {
return !stripped.chars().all(|c| c.is_ascii_digit());
}
true
})
.collect::<Vec<_>>()
.join(" ");
s.trim().to_string()
}
fn to_wide(s: &str) -> Vec<u16> {
use std::os::windows::ffi::OsStrExt;
std::ffi::OsStr::new(s)
.encode_wide()
.chain(std::iter::once(0))
.collect()
}
fn read_reg_sz(root: winapi::shared::minwindef::HKEY, subkey: &str, value: &str) -> Option<String> {
let sub = to_wide(subkey);
let val = to_wide(value);
let mut size: u32 = 0;
let status = unsafe {
RegGetValueW(
root,
sub.as_ptr(),
val.as_ptr(),
RRF_RT_REG_SZ,
null_mut(),
null_mut(),
&mut size,
)
};
if status != ERROR_SUCCESS as i32 || size == 0 {
return None;
}
let len_wchars = (size as usize + 1) / 2; let mut buf: Vec<u16> = vec![0u16; len_wchars];
let mut size2 = size;
let status = unsafe {
RegGetValueW(
root,
sub.as_ptr(),
val.as_ptr(),
RRF_RT_REG_SZ,
null_mut(),
buf.as_mut_ptr() as *mut _,
&mut size2,
)
};
if status != ERROR_SUCCESS as i32 {
return None;
}
let end = buf.iter().position(|&c| c == 0).unwrap_or(buf.len());
Some(String::from_utf16_lossy(&buf[..end]).trim().to_string())
}