tmux_applets/
cpu.rs

1use std::fmt;
2use std::fs;
3
4use nix::unistd;
5
6use colorsys::Rgb;
7
8use crate::common::{parse_colour_param, pct_value_hsl};
9
10#[derive(Debug, PartialEq)]
11pub enum CPUAppletError {
12    CPUCount,
13    CPUInfo,
14}
15
16impl std::error::Error for CPUAppletError {}
17
18impl fmt::Display for CPUAppletError {
19    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
20        write!(f, "CPUAppletError: {:?}", self)
21    }
22}
23
24type Result<T> = std::result::Result<T, CPUAppletError>;
25
26pub fn cpu_count() -> Result<u32> {
27    let count = match unistd::sysconf(unistd::SysconfVar::_NPROCESSORS_ONLN) {
28        Ok(Some(c)) => c as u32,
29        Ok(None) => return Err(CPUAppletError::CPUCount),
30        Err(_) => return Err(CPUAppletError::CPUCount),
31    };
32    Ok(count)
33}
34
35#[derive(Debug, PartialEq)]
36pub struct CPUInfo {
37    pub min_freq: u32,
38    pub max_freq: u32,
39    pub cur_freq: u32,
40}
41
42fn read_u32_from_file(path: &str) -> Option<u32> {
43    let vstr = match fs::read_to_string(path) {
44        Ok(v) => v,
45        Err(_) => return None,
46    };
47
48    let vstr = vstr.trim_end();
49
50    vstr.parse::<u32>().ok()
51}
52
53// convert the current cpu frequency into a percentage, range [0.0, 1.0]
54fn normalise_cur_freq(cpu: &CPUInfo) -> f32 {
55    let mut c = cpu.cur_freq;
56    if c < cpu.min_freq {
57        c = cpu.min_freq
58    }
59    if c > cpu.max_freq {
60        c = cpu.max_freq
61    }
62    let range = cpu.max_freq - cpu.min_freq;
63    let adj = c - cpu.min_freq;
64    adj as f32 / range as f32
65}
66
67fn cpu_info(cpu_index: u32) -> Result<CPUInfo> {
68    let min_freq_path = format!("/sys/bus/cpu/devices/cpu{cpu_index}/cpufreq/scaling_min_freq");
69    let max_freq_path = format!("/sys/bus/cpu/devices/cpu{cpu_index}/cpufreq/scaling_max_freq");
70    let cur_freq_path = format!("/sys/bus/cpu/devices/cpu{cpu_index}/cpufreq/scaling_cur_freq");
71
72    match (read_u32_from_file(&min_freq_path), read_u32_from_file(&max_freq_path), read_u32_from_file(&cur_freq_path)) {
73        (Some(min), Some(max), Some(cur)) => Ok(CPUInfo { min_freq: min, max_freq: max, cur_freq: cur }),
74        _ => Err(CPUAppletError::CPUInfo),
75    }
76}
77
78impl fmt::Display for CPUInfo {
79    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
80        write!(f, "CPUInfo: {:?}", self)
81    }
82}
83
84pub fn applet(args: &[String]) -> Result<()> {
85    let mut colour_s: Option<f32> = None;
86    let mut colour_l: Option<f32> = None;
87
88    for arg in args {
89        if let Some(s) = parse_colour_param(arg, "s") {
90            if (0.0..=100.0).contains(&s) {
91                colour_s = Some(s);
92            } else {
93                eprintln!("Saturation {s} out of range [0, 100.0]");
94            }
95        };
96        if let Some(l) = parse_colour_param(arg, "l") {
97            if (0.0..=100.0).contains(&l) {
98                colour_l = Some(l);
99            } else {
100                eprintln!("Lightness {l} out of range [0, 100.0]");
101            }
102        }
103    }
104
105    eprintln!("Saturation: {:?} Lightness: {:?}", colour_s, colour_l);
106
107    let c = cpu_count()?;
108
109    for i in 0..c {
110        let info = cpu_info(i)?;
111        let norm = normalise_cur_freq(&info) * 100.0;
112
113        let c = pct_value_hsl(norm, colour_s, colour_l);
114        let rgb = Rgb::from(&c);
115
116        eprintln!("CPU {i} Info: {info} Norm: {norm:.0}% RGB: {}", rgb.to_hex_string());
117
118        print!("#[bg={}]  ", rgb.to_hex_string());
119    }
120    println!("#[default]");
121
122    Ok(())
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128
129    #[test]
130    fn test_normalise_cur_freq() {
131        assert_eq!(0.0, normalise_cur_freq(&CPUInfo { min_freq: 100, max_freq: 200, cur_freq: 99 }));
132        assert_eq!(0.0, normalise_cur_freq(&CPUInfo { min_freq: 100, max_freq: 200, cur_freq: 100 }));
133        assert_eq!(0.5, normalise_cur_freq(&CPUInfo { min_freq: 100, max_freq: 200, cur_freq: 150 }));
134        assert_eq!(1.0, normalise_cur_freq(&CPUInfo { min_freq: 100, max_freq: 200, cur_freq: 201 }));
135    }
136}