diff --git a/local/recipes/libs/libxau/recipe.toml b/local/recipes/libs/libxau/recipe.toml index 5593726558..7dec613687 100644 --- a/local/recipes/libs/libxau/recipe.toml +++ b/local/recipes/libs/libxau/recipe.toml @@ -3,12 +3,11 @@ tar = "https://www.x.org/releases/individual/lib/libXau-1.0.12.tar.xz" blake3 = "674bc71a888eec20f0e29989e4669df90309d4baacad058107cdf89d23803bcc" [build] -template = "configure" +template = "custom" dependencies = [ "x11proto", ] - -[build.script] +script = """ DYNAMIC_INIT # x11proto provides the X11 protocol headers needed by libxau @@ -27,3 +26,4 @@ export PKG_CONFIG_PATH="${COOKBOOK_SYSROOT}/usr/share/pkgconfig:${COOKBOOK_SYSRO make -j"${COOKBOOK_MAKE_JOBS}" make DESTDIR="${COOKBOOK_STAGE}" install +""" diff --git a/local/recipes/system/redbear-power/source/src/acpi.rs b/local/recipes/system/redbear-power/source/src/acpi.rs index c9b343d803..c2227b57d9 100644 --- a/local/recipes/system/redbear-power/source/src/acpi.rs +++ b/local/recipes/system/redbear-power/source/src/acpi.rs @@ -43,25 +43,62 @@ pub fn detect_cpus() -> Vec { } /// Compute per-CPU load fraction (0.0..=1.0) using the delta -/// between two `/scheme/sys/cpu/{n}/stat` reads. Returns 0.0 on -/// first sample (no prior tick to delta against). +/// between two reads. Tries Redox `/scheme/sys/cpu/{n}/stat` first, +/// falls back to Linux `/proc/stat` per-CPU lines (`cpuN user nice +/// system idle ...`). Returns 0.0 on first sample (no prior tick +/// to delta against). pub fn read_load(cpu: u32, prev: &mut (u64, u64)) -> f64 { - let path = format!("/scheme/sys/cpu/{}/stat", cpu); - let Ok(data) = fs::read_to_string(&path) else { return 0.0 }; + // Try Redox per-CPU stat first. + let redox_path = format!("/scheme/sys/cpu/{}/stat", cpu); + if let Some((total, idle)) = read_redox_stat(&redox_path) { + return delta_load(total, idle, prev); + } + // Linux fallback: /proc/stat aggregates all CPUs in one file. + let cpu_total_idle = read_proc_stat_cpu(cpu); + if let Some((total, idle)) = cpu_total_idle { + return delta_load(total, idle, prev); + } + 0.0 +} + +fn read_redox_stat(path: &str) -> Option<(u64, u64)> { + let data = fs::read_to_string(path).ok()?; let p: Vec = data .split_whitespace() .filter_map(|s| s.parse().ok()) .collect(); if p.len() < 4 { - return 0.0; + return None; } let total: u64 = p.iter().sum(); - let idle = p[3]; + Some((total, p[3])) +} + +/// Read `/proc/stat` and extract the per-CPU aggregate for `cpu`. +/// Linux `/proc/stat` first line is `cpu user nice system idle ...`, +/// subsequent lines are `cpu0 ...`, `cpu1 ...`, etc. +fn read_proc_stat_cpu(cpu: u32) -> Option<(u64, u64)> { + let data = fs::read_to_string("/proc/stat").ok()?; + let label = format!("cpu{}", cpu); + for line in data.lines() { + if line.starts_with(&label) { + let rest = &line[label.len()..]; + let p: Vec = rest + .split_whitespace() + .filter_map(|s| s.parse().ok()) + .collect(); + if p.len() >= 4 { + let total: u64 = p.iter().sum(); + return Some((total, p[3])); + } + } + } + None +} + +fn delta_load(total: u64, idle: u64, prev: &mut (u64, u64)) -> f64 { let (p_total, p_idle) = *prev; *prev = (total, idle); - // First sample: no prior tick to delta against; a cumulative - // fraction would read as ~99% on any system that has been up - // for any time. Wait for the next refresh. if p_total == 0 { return 0.0; } @@ -75,37 +112,68 @@ pub fn read_load(cpu: u32, prev: &mut (u64, u64)) -> f64 { } pub fn read_acpi_pss(cpu: u32) -> Vec { - let path = format!("/scheme/acpi/processor/CPU{}/pss", cpu); - if let Ok(s) = fs::read_to_string(&path) { + // Try Redox ACPI _PSS first. + let scheme_path = format!("/scheme/acpi/processor/CPU{}/pss", cpu); + if let Ok(s) = fs::read_to_string(&scheme_path) { + if let Some(out) = parse_pss(&s) { + return out; + } + } + // Linux fallback: /sys/devices/system/cpu/cpu{N}/cpufreq/scaling_available_frequencies + // (kHz strings separated by spaces, e.g. "2400000 2200000 1900000 ..."). + // Power values are not exposed by sysfs — use a placeholder of 0 W + // which renders as "0.0" in the per-CPU PkgW column. + let sysfs_path = format!( + "/sys/devices/system/cpu/cpu{}/cpufreq/scaling_available_frequencies", + cpu + ); + if let Ok(s) = fs::read_to_string(&sysfs_path) { let mut out = Vec::new(); - for line in s.lines() { - let w: Vec<&str> = line.split_whitespace().collect(); - if w.len() >= 6 { - if let (Ok(f), Ok(pw), Ok(ct)) = ( - w[0].parse::(), - w[2].parse::(), - u64::from_str_radix(w[5], 16), - ) { - out.push(PState { freq_khz: f, power_mw: pw, ctl: ct }); - } - } + for (idx, token) in s.split_whitespace().enumerate() { + let freq_khz: u32 = match token.parse() { + Ok(f) => f, + Err(_) => continue, + }; + // P-state index in bits 14:8 of IA32_PERF_CTL. The CTL value + // computed here is for documentation only; we cannot write + // to IA32_PERF_CTL from sysfs context (kernel ACPI cpufreq + // driver handles transitions). + let ctl: u64 = (idx as u64) << 8; + out.push(PState { + freq_khz, + power_mw: 0, + ctl, + }); } if !out.is_empty() { return out; } } - // Fallback: P0..P5 covering the typical Intel notebook range. The - // ctl field is the IA32_PERF_CTL value with the P-state index in - // bits 14:8 (per Intel SDM Vol. 4); these defaults let the TUI - // display freq/power when ACPI _PSS is missing (QEMU, some laptops). - vec![ - PState { freq_khz: 2400, power_mw: 15000, ctl: 0x0000 }, // P0 max - PState { freq_khz: 2200, power_mw: 13000, ctl: 0x0100 }, - PState { freq_khz: 1900, power_mw: 11000, ctl: 0x0200 }, - PState { freq_khz: 1600, power_mw: 9000, ctl: 0x0400 }, - PState { freq_khz: 1300, power_mw: 7500, ctl: 0x0800 }, - PState { freq_khz: 900, power_mw: 5000, ctl: 0x1000 }, // P5 min - ] + // No real data anywhere. Return empty so the render layer can show + // "—" instead of fake numbers. This satisfies the zero-stub policy: + // never invent data we cannot measure. + Vec::new() +} + +fn parse_pss(s: &str) -> Option> { + let mut out = Vec::new(); + for line in s.lines() { + let w: Vec<&str> = line.split_whitespace().collect(); + if w.len() >= 6 { + if let (Ok(f), Ok(pw), Ok(ct)) = ( + w[0].parse::(), + w[2].parse::(), + u64::from_str_radix(w[5], 16), + ) { + out.push(PState { freq_khz: f, power_mw: pw, ctl: ct }); + } + } + } + if out.is_empty() { + None + } else { + Some(out) + } } /// Read CPU vendor and model strings, preferring Redox diff --git a/local/recipes/tui/tlc/source/src/terminal/color.rs b/local/recipes/tui/tlc/source/src/terminal/color.rs index 3021a32de0..b66dd833f4 100644 --- a/local/recipes/tui/tlc/source/src/terminal/color.rs +++ b/local/recipes/tui/tlc/source/src/terminal/color.rs @@ -232,12 +232,12 @@ impl Theme { let normalized = match name { "" | "default-dark" => "julia256", "default-light" | "light" => "sand256", - "high-contrast" => "mc-classic", + "high-contrast" => "default", "solarized-dark" => "seasons-summer16M", "nord" => "nicedark", - "mc-classic" => "default", - "mc-dark" => "dark", - "mc-dark-gray" => "nicedark", + "mc-classic" | "default" => "default", + "mc-dark" | "dark" => "dark", + "mc-dark-gray" | "nicedark" => "nicedark", other => other, }; if let Some(theme) = super::mc_skin::theme_by_name(normalized) {