josie / alder-tools

TUI: freq bars span per-core min-max window (idle floor = 0%)

ff0bca334c307f877f251c9d848abac5e88a4078
josie <administrator@josie-c.com> · 2026-08-29T12:32 · browse files at this commit

parents: a4b120f

diff --git a/src/sensors.rs b/src/sensors.rs
index 1b89878..d243d48 100644
--- a/src/sensors.rs
+++ b/src/sensors.rs
@@ -122,6 +122,16 @@ pub fn cpu_frequencies() -> Vec<(usize, u64)> {
     out
 }
 
+/// Min frequency (kHz) advertised for a logical cpu (its idle floor).
+pub fn cpu_min_freq(cpu: usize) -> Option<u64> {
+    read_int(
+        &Path::new("/sys/devices/system/cpu")
+            .join(format!("cpu{cpu}"))
+            .join("cpufreq/cpuinfo_min_freq"),
+    )
+    .map(|v| v as u64)
+}
+
 /// Max frequency (kHz) advertised for a logical cpu (its turbo ceiling).
 pub fn cpu_max_freq(cpu: usize) -> Option<u64> {
     read_int(
diff --git a/src/ui.rs b/src/ui.rs
index 0e47b15..7d572ee 100644
--- a/src/ui.rs
+++ b/src/ui.rs
@@ -46,6 +46,7 @@ struct CoreRow {
     label: String,
     e_core: bool,
     freq_khz: Option<u64>,
+    freq_min_khz: u64,
     freq_max_khz: u64,
     temp: Option<f64>,
 }
@@ -103,7 +104,7 @@ impl App {
                         .and_then(|d| d.parse().ok())
                         .unwrap_or(u64::MAX);
                     let cpus = core_to_cpus(core_id);
-                    let e_core = cpus.first().map_or(false, |c| *c >= 12);
+                    let e_core = cpus.first().is_some_and(|c| *c >= 12);
                     let freq_khz = cpus
                         .iter()
                         .filter_map(|c| {
@@ -115,10 +116,16 @@ impl App {
                         .filter_map(|c| sensors::cpu_max_freq(*c))
                         .max()
                         .unwrap_or(0);
+                    let freq_min_khz = cpus
+                        .iter()
+                        .filter_map(|c| sensors::cpu_min_freq(*c))
+                        .max()
+                        .unwrap_or(0);
                     CoreRow {
                         label: label.clone(),
                         e_core,
                         freq_khz,
+                        freq_min_khz,
                         freq_max_khz,
                         temp: Some(*t),
                     }
@@ -323,8 +330,14 @@ pub fn draw(f: &mut Frame, app: &App) {
                 (None, Some(t)) => format!("{:.0}°C", t),
                 (None, None) => "n/a".to_string(),
             };
+            // Bar spans the cpu's own [min_freq, max_freq] window so the
+            // 800 MHz idle floor sits at 0% and turbo pegs 100%.
             let ratio = match (core.freq_khz, core.freq_max_khz) {
-                (Some(f), max) if max > 0 => Some(f as f64 / max as f64),
+                (Some(f), max) if max > core.freq_min_khz => Some(
+                    ((f as f64 - core.freq_min_khz as f64)
+                        / (max - core.freq_min_khz) as f64)
+                        .clamp(0.0, 1.0),
+                ),
                 _ => None,
             };
             let color = match core.temp {