redbear-power: auto-fix 84 clippy warnings (100→16)
Mechanical fixes: collapsible-if, is_multiple_of, sort_by_key, needless borrows, useless format!, manual checked division, push-after-creation, and other style lints. Zero behavioral changes. Remaining 16 warnings are pre-existing intentional patterns (identical if-blocks from match arms, &mut Vec params).
This commit is contained in:
@@ -33,20 +33,20 @@ pub fn read_cpu_freq_khz_sysfs(cpu: u32) -> Option<u32> {
|
||||
"/sys/devices/system/cpu/cpu{}/cpufreq/scaling_cur_freq",
|
||||
cpu
|
||||
);
|
||||
if let Ok(s) = fs::read_to_string(&path) {
|
||||
if let Ok(khz) = s.trim().parse::<u32>() {
|
||||
return Some(khz);
|
||||
}
|
||||
if let Ok(s) = fs::read_to_string(&path)
|
||||
&& let Ok(khz) = s.trim().parse::<u32>()
|
||||
{
|
||||
return Some(khz);
|
||||
}
|
||||
// Fallback: cpuinfo_cur_freq (some drivers expose this instead).
|
||||
let path2 = format!(
|
||||
"/sys/devices/system/cpu/cpu{}/cpufreq/cpuinfo_cur_freq",
|
||||
cpu
|
||||
);
|
||||
if let Ok(s) = fs::read_to_string(&path2) {
|
||||
if let Ok(khz) = s.trim().parse::<u32>() {
|
||||
return Some(khz);
|
||||
}
|
||||
if let Ok(s) = fs::read_to_string(&path2)
|
||||
&& let Ok(khz) = s.trim().parse::<u32>()
|
||||
{
|
||||
return Some(khz);
|
||||
}
|
||||
None
|
||||
}
|
||||
@@ -69,16 +69,16 @@ pub fn read_rapl_package_energy() -> Option<(u64, Instant)> {
|
||||
}
|
||||
// 2. Fallback: Linux powercap sysfs
|
||||
let path = "/sys/class/powercap/intel-rapl:0/energy_uj";
|
||||
if let Ok(s) = fs::read_to_string(path) {
|
||||
if let Ok(uj) = s.trim().parse::<u64>() {
|
||||
return Some((uj, Instant::now()));
|
||||
}
|
||||
if let Ok(s) = fs::read_to_string(path)
|
||||
&& let Ok(uj) = s.trim().parse::<u64>()
|
||||
{
|
||||
return Some((uj, Instant::now()));
|
||||
}
|
||||
let path2 = "/sys/class/powercap/intel-rapl/intel-rapl:0/energy_uj";
|
||||
if let Ok(s) = fs::read_to_string(path2) {
|
||||
if let Ok(uj) = s.trim().parse::<u64>() {
|
||||
return Some((uj, Instant::now()));
|
||||
}
|
||||
if let Ok(s) = fs::read_to_string(path2)
|
||||
&& let Ok(uj) = s.trim().parse::<u64>()
|
||||
{
|
||||
return Some((uj, Instant::now()));
|
||||
}
|
||||
None
|
||||
}
|
||||
@@ -94,12 +94,7 @@ pub fn rapl_power_watts(curr: (u64, Instant), prev: (u64, Instant)) -> (f64, (u6
|
||||
if dt < Duration::from_millis(100) || dt > Duration::from_secs(5) {
|
||||
return (0.0, curr);
|
||||
}
|
||||
let de = if e_curr >= e_prev {
|
||||
e_curr - e_prev
|
||||
} else {
|
||||
// Energy counter wrapped (unlikely at u64 microjoules, but handle it).
|
||||
0
|
||||
};
|
||||
let de = e_curr.saturating_sub(e_prev);
|
||||
let watts = (de as f64 / 1_000_000.0) / dt.as_secs_f64();
|
||||
(watts, curr)
|
||||
}
|
||||
@@ -119,12 +114,11 @@ pub fn detect_cpus() -> Vec<u32> {
|
||||
// not exist on Redox. Linux falls back to /dev/cpu/N entries.
|
||||
if let Ok(data) = fs::read_to_string("/scheme/sys/cpu") {
|
||||
for line in data.lines() {
|
||||
if let Some(rest) = line.strip_prefix("CPUs: ") {
|
||||
if let Ok(n) = rest.trim().parse::<u32>() {
|
||||
if n > 0 {
|
||||
return (0..n).collect();
|
||||
}
|
||||
}
|
||||
if let Some(rest) = line.strip_prefix("CPUs: ")
|
||||
&& let Ok(n) = rest.trim().parse::<u32>()
|
||||
&& n > 0
|
||||
{
|
||||
return (0..n).collect();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -214,10 +208,10 @@ fn delta_load(total: u64, idle: u64, prev: &mut (u64, u64)) -> f64 {
|
||||
pub fn read_acpi_pss(cpu: u32) -> Vec<PState> {
|
||||
// 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;
|
||||
}
|
||||
if let Ok(s) = fs::read_to_string(&scheme_path)
|
||||
&& 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 ...").
|
||||
@@ -263,25 +257,23 @@ pub fn read_acpi_pss(cpu: u32) -> Vec<PState> {
|
||||
"/sys/devices/system/cpu/cpu{}/cpufreq/cpuinfo_max_freq",
|
||||
cpu
|
||||
);
|
||||
if let (Ok(min_s), Ok(max_s)) = (fs::read_to_string(&min_path), fs::read_to_string(&max_path)) {
|
||||
if let (Ok(min_khz), Ok(max_khz)) =
|
||||
if let (Ok(min_s), Ok(max_s)) = (fs::read_to_string(&min_path), fs::read_to_string(&max_path))
|
||||
&& let (Ok(min_khz), Ok(max_khz)) =
|
||||
(min_s.trim().parse::<u32>(), max_s.trim().parse::<u32>())
|
||||
{
|
||||
if max_khz > min_khz {
|
||||
let steps: u32 = 5;
|
||||
let mut out = Vec::with_capacity(steps as usize + 1);
|
||||
for i in 0..=steps {
|
||||
let freq = min_khz + (max_khz - min_khz) * i / steps;
|
||||
let ctl = (i as u64) << 8;
|
||||
out.push(PState {
|
||||
freq_khz: freq,
|
||||
power_mw: 0,
|
||||
ctl,
|
||||
});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
&& max_khz > min_khz
|
||||
{
|
||||
let steps: u32 = 5;
|
||||
let mut out = Vec::with_capacity(steps as usize + 1);
|
||||
for i in 0..=steps {
|
||||
let freq = min_khz + (max_khz - min_khz) * i / steps;
|
||||
let ctl = (i as u64) << 8;
|
||||
out.push(PState {
|
||||
freq_khz: freq,
|
||||
power_mw: 0,
|
||||
ctl,
|
||||
});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
Vec::new()
|
||||
}
|
||||
@@ -290,18 +282,18 @@ fn parse_pss(s: &str) -> Option<Vec<PState>> {
|
||||
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)) = (
|
||||
if w.len() >= 6
|
||||
&& let (Ok(f), Ok(pw), Ok(ct)) = (
|
||||
w[0].parse::<u32>(),
|
||||
w[2].parse::<u32>(),
|
||||
u64::from_str_radix(w[5], 16),
|
||||
) {
|
||||
out.push(PState {
|
||||
freq_khz: f,
|
||||
power_mw: pw,
|
||||
ctl: ct,
|
||||
});
|
||||
}
|
||||
)
|
||||
{
|
||||
out.push(PState {
|
||||
freq_khz: f,
|
||||
power_mw: pw,
|
||||
ctl: ct,
|
||||
});
|
||||
}
|
||||
}
|
||||
if out.is_empty() { None } else { Some(out) }
|
||||
@@ -328,15 +320,11 @@ pub fn read_cpu_id() -> Option<(String, String)> {
|
||||
continue;
|
||||
}
|
||||
match key.as_str() {
|
||||
"vendor_id" | "vendor" | "manufacturer" => {
|
||||
if vendor.is_empty() {
|
||||
vendor = val;
|
||||
}
|
||||
"vendor_id" | "vendor" | "manufacturer" if vendor.is_empty() => {
|
||||
vendor = val;
|
||||
}
|
||||
"model name" | "model" | "product name" | "cpu" | "hardware" => {
|
||||
if model.is_empty() {
|
||||
model = val;
|
||||
}
|
||||
"model name" | "model" | "product name" | "cpu" | "hardware" if model.is_empty() => {
|
||||
model = val;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -572,10 +572,10 @@ impl App {
|
||||
// without hammering /proc/meminfo on every tick.
|
||||
self.refresh_counter = self.refresh_counter.wrapping_add(1);
|
||||
// Clear MSR failure cache every ~30s so permission changes are picked up.
|
||||
if self.refresh_counter % 60 == 0 {
|
||||
if self.refresh_counter.is_multiple_of(60) {
|
||||
crate::msr::clear_msr_cache();
|
||||
}
|
||||
if self.refresh_counter % 4 == 0 {
|
||||
if self.refresh_counter.is_multiple_of(4) {
|
||||
self.meminfo = crate::meminfo::read_meminfo();
|
||||
self.os_info = crate::meminfo::read_os_info();
|
||||
self.load_avg = std::fs::read_to_string("/proc/loadavg").ok().and_then(|s| {
|
||||
@@ -598,7 +598,7 @@ impl App {
|
||||
// Battery tab stays useful without hammering sysfs at 2 Hz.
|
||||
// On desktops without a battery, find_battery_dir() returns None
|
||||
// in ~1 ms; the cost is negligible.
|
||||
if self.refresh_counter % 5 == 0 {
|
||||
if self.refresh_counter.is_multiple_of(5) {
|
||||
self.battery = crate::battery::BatteryInfo::read();
|
||||
}
|
||||
|
||||
@@ -607,7 +607,7 @@ impl App {
|
||||
// modulus is coprime to meminfo's 4-tick and battery's 5-tick
|
||||
// moduli, so sensor reads don't synchronize with other data
|
||||
// sources (no thundering-herd syscalls).
|
||||
if self.refresh_counter % 3 == 0 {
|
||||
if self.refresh_counter.is_multiple_of(3) {
|
||||
self.sensors = crate::sensor::SensorInfo::read();
|
||||
}
|
||||
|
||||
@@ -617,7 +617,7 @@ impl App {
|
||||
// so network reads don't synchronize with any other data
|
||||
// source. 3.5 sec cadence is the same order as cpu-x's
|
||||
// network panel update rate.
|
||||
if self.refresh_counter % 7 == 0 {
|
||||
if self.refresh_counter.is_multiple_of(7) {
|
||||
let now_secs = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.map(|d| d.as_secs_f64())
|
||||
@@ -653,7 +653,7 @@ impl App {
|
||||
// read_bytes/write_bytes vs previous 11th-tick refresh,
|
||||
// divided by elapsed wall time. SMART data is also refreshed
|
||||
// at the same cadence since it pairs naturally with disk info.
|
||||
if self.refresh_counter % 11 == 0 {
|
||||
if self.refresh_counter.is_multiple_of(11) {
|
||||
let now_secs = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.map(|d| d.as_secs_f64())
|
||||
@@ -691,7 +691,7 @@ impl App {
|
||||
// 7, 11), so process reads don't synchronize with any other
|
||||
// data source. 6.5 sec is sufficient because process state
|
||||
// changes are mostly visible at human-perceptual timescales.
|
||||
if self.refresh_counter % 13 == 0 {
|
||||
if self.refresh_counter.is_multiple_of(13) {
|
||||
let now_secs = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.map(|d| d.as_secs_f64())
|
||||
|
||||
@@ -71,10 +71,10 @@ impl BatteryInfo {
|
||||
let dir = fs::read_dir(&search_root).ok()?;
|
||||
for entry in dir.flatten() {
|
||||
let type_path = entry.path().join("type");
|
||||
if let Some(t) = read_sysfs(&type_path) {
|
||||
if t == "Battery" {
|
||||
return Some(entry.path());
|
||||
}
|
||||
if let Some(t) = read_sysfs(&type_path)
|
||||
&& t == "Battery"
|
||||
{
|
||||
return Some(entry.path());
|
||||
}
|
||||
}
|
||||
None
|
||||
|
||||
@@ -135,7 +135,7 @@ fn prime_worker(cancel: &AtomicBool, duration: Duration) -> u64 {
|
||||
let mut is_prime = n >= 2;
|
||||
let mut i: u64 = 2;
|
||||
while i * i <= n && is_prime {
|
||||
if n % i == 0 {
|
||||
if n.is_multiple_of(i) {
|
||||
is_prime = false;
|
||||
}
|
||||
i += 1;
|
||||
|
||||
@@ -26,6 +26,7 @@ use serde::Deserialize;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[serde(default)]
|
||||
#[derive(Default)]
|
||||
pub struct Config {
|
||||
pub display: DisplayConfig,
|
||||
pub theme: ThemeConfig,
|
||||
@@ -33,17 +34,6 @@ pub struct Config {
|
||||
pub benchmark: BenchmarkConfig,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
display: DisplayConfig::default(),
|
||||
theme: ThemeConfig::default(),
|
||||
keybindings: KeyBindings::default(),
|
||||
benchmark: BenchmarkConfig::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct DisplayConfig {
|
||||
|
||||
@@ -210,11 +210,11 @@ impl Cpufreq {
|
||||
continue;
|
||||
}
|
||||
// Verify.
|
||||
if let Some(actual) = backend.read_active() {
|
||||
if actual == *candidate {
|
||||
self.active = candidate.clone();
|
||||
return Some(candidate.clone());
|
||||
}
|
||||
if let Some(actual) = backend.read_active()
|
||||
&& actual == *candidate
|
||||
{
|
||||
self.active = candidate.clone();
|
||||
return Some(candidate.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
@@ -228,11 +228,11 @@ impl Cpufreq {
|
||||
if !backend.write(name) {
|
||||
return false;
|
||||
}
|
||||
if let Some(actual) = backend.read_active() {
|
||||
if actual == name {
|
||||
self.active = name.to_string();
|
||||
return true;
|
||||
}
|
||||
if let Some(actual) = backend.read_active()
|
||||
&& actual == name
|
||||
{
|
||||
self.active = name.to_string();
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
@@ -270,10 +270,11 @@ fn discover_by_probing(backend: &Backend) -> Vec<String> {
|
||||
if !backend.write(candidate) {
|
||||
continue;
|
||||
}
|
||||
if let Some(actual) = backend.read_active() {
|
||||
if actual == *candidate && !found.contains(&candidate.to_string()) {
|
||||
found.push(candidate.to_string());
|
||||
}
|
||||
if let Some(actual) = backend.read_active()
|
||||
&& actual == *candidate
|
||||
&& !found.contains(&candidate.to_string())
|
||||
{
|
||||
found.push(candidate.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ pub struct CpuId {
|
||||
/// Intel hybrid CPU (12th gen+) identifies P-cores and E-cores via
|
||||
/// CPUID leaf 0x1A; AMD Zen 2+ identifies CCDs via 0x8000001E. Both
|
||||
/// schemes map each logical processor to a `CoreType` for grouping.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
|
||||
pub enum CoreType {
|
||||
/// Intel Performance core (Raptor Lake / Alder Lake).
|
||||
IntelP,
|
||||
@@ -45,15 +45,10 @@ pub enum CoreType {
|
||||
/// The CCD number is stored separately.
|
||||
AmdCcd(u8),
|
||||
/// Unknown / vendor without hybrid support.
|
||||
#[default]
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl Default for CoreType {
|
||||
fn default() -> Self {
|
||||
CoreType::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct HybridInfo {
|
||||
pub is_hybrid: bool,
|
||||
@@ -251,20 +246,20 @@ fn detect_hybrid(max_leaf: u32, max_ext: u32, num_cpus: u32, vendor: &str) -> Ve
|
||||
// which is our display group. For Zen 4+, leaf 0x80000026 exposes
|
||||
// CCD-level counts directly; we honor that when available.
|
||||
let (_, ebx_1e, _, _) = raw::cpuid(0x8000_001e, 0);
|
||||
let nc = ((ebx_1e >> 8) & 0xff) as u32;
|
||||
let nc = (ebx_1e >> 8) & 0xff;
|
||||
let cores_per_ccx = if nc > 0 { nc as usize } else { 1 };
|
||||
let (zen4_ccds, _zen4_cores_per_ccd) = if max_ext >= 0x8000_0026 {
|
||||
// Zen 4+ topology leaf: EAX bits 7:0 = # of enabled CCDs,
|
||||
// ECX bits 7:0 = # of cores per CCD (0 = not supported).
|
||||
let (eax_26, _ebx_26, ecx_26, _edx_26) = raw::cpuid(0x8000_0026, 0);
|
||||
((eax_26 & 0xff) as u32, (ecx_26 & 0xff) as u32)
|
||||
((eax_26 & 0xff), (ecx_26 & 0xff))
|
||||
} else {
|
||||
(0, 0)
|
||||
};
|
||||
let mut out = Vec::with_capacity(num_cpus as usize);
|
||||
for cpu in 0..num_cpus {
|
||||
if zen4_ccds > 0 {
|
||||
let ccd_id = cpu / _zen4_cores_per_ccd.max(1) as u32;
|
||||
let ccd_id = cpu / _zen4_cores_per_ccd.max(1);
|
||||
out.push((cpu, CoreType::AmdCcd(ccd_id as u8)));
|
||||
} else {
|
||||
let ccd_id = cpu / cores_per_ccx.max(1) as u32;
|
||||
|
||||
@@ -108,17 +108,18 @@ impl Widget for BrailleGraph<'_> {
|
||||
.render(canvas_area, buf);
|
||||
|
||||
// Render y-axis labels in the reserved left margin.
|
||||
if let Some((ref top_label, ref bot_label)) = self.y_labels {
|
||||
if inner.height >= 2 && inner.width >= 4 {
|
||||
let label_style = Style::new().dark_gray();
|
||||
// Top label: right-aligned in 4-char space.
|
||||
let top = format!("{:>4}", top_label.chars().take(4).collect::<String>());
|
||||
buf.set_string(inner.x, inner.y, &top, label_style);
|
||||
// Bottom label.
|
||||
let bot_y = inner.y + inner.height.saturating_sub(1);
|
||||
let bot = format!("{:>4}", bot_label.chars().take(4).collect::<String>());
|
||||
buf.set_string(inner.x, bot_y, &bot, label_style);
|
||||
}
|
||||
if let Some((ref top_label, ref bot_label)) = self.y_labels
|
||||
&& inner.height >= 2
|
||||
&& inner.width >= 4
|
||||
{
|
||||
let label_style = Style::new().dark_gray();
|
||||
// Top label: right-aligned in 4-char space.
|
||||
let top = format!("{:>4}", top_label.chars().take(4).collect::<String>());
|
||||
buf.set_string(inner.x, inner.y, &top, label_style);
|
||||
// Bottom label.
|
||||
let bot_y = inner.y + inner.height.saturating_sub(1);
|
||||
let bot = format!("{:>4}", bot_label.chars().take(4).collect::<String>());
|
||||
buf.set_string(inner.x, bot_y, &bot, label_style);
|
||||
}
|
||||
|
||||
// Render optional x-axis labels.
|
||||
|
||||
@@ -77,10 +77,10 @@ impl KillDialog {
|
||||
}
|
||||
|
||||
pub fn auto_close_if_done(&mut self) {
|
||||
if let Some(at) = self.result_at {
|
||||
if at.elapsed().as_millis() > 2000 {
|
||||
self.close();
|
||||
}
|
||||
if let Some(at) = self.result_at
|
||||
&& at.elapsed().as_millis() > 2000
|
||||
{
|
||||
self.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,19 +184,19 @@ fn handle_mouse(me: MouseEvent, header: &Rect, table: &Rect, keybar: &Rect, app:
|
||||
// Header line is y == table.y; rows start at y == table.y + 1.
|
||||
let row = y.saturating_sub(table.y + 1) as usize;
|
||||
let id = app.cpus.get(row).map(|c| c.id);
|
||||
if let Some(id) = id {
|
||||
if let Some(idx) = app.cpus.iter().position(|c| c.id == id) {
|
||||
app.table_state.select(Some(idx));
|
||||
// Re-click on the same row toggles expand
|
||||
// (mirrors htop). A click on a different
|
||||
// row just selects that row.
|
||||
if app.last_clicked_cpu == Some(id) {
|
||||
app.toggle_expand();
|
||||
} else {
|
||||
app.expanded_cpu = None;
|
||||
}
|
||||
app.last_clicked_cpu = Some(id);
|
||||
if let Some(id) = id
|
||||
&& let Some(idx) = app.cpus.iter().position(|c| c.id == id)
|
||||
{
|
||||
app.table_state.select(Some(idx));
|
||||
// Re-click on the same row toggles expand
|
||||
// (mirrors htop). A click on a different
|
||||
// row just selects that row.
|
||||
if app.last_clicked_cpu == Some(id) {
|
||||
app.toggle_expand();
|
||||
} else {
|
||||
app.expanded_cpu = None;
|
||||
}
|
||||
app.last_clicked_cpu = Some(id);
|
||||
}
|
||||
} else if hit_test(*header, x, y) {
|
||||
// Header click cycles the governor (the most common
|
||||
@@ -271,7 +271,7 @@ fn main() -> io::Result<()> {
|
||||
let theme_mode = args
|
||||
.theme_mode
|
||||
.as_deref()
|
||||
.unwrap_or_else(|| cfg.theme.mode.as_str());
|
||||
.unwrap_or(cfg.theme.mode.as_str());
|
||||
app.theme_idx = match theme_mode {
|
||||
"light" => 1,
|
||||
"high-contrast" => 2,
|
||||
@@ -395,18 +395,18 @@ fn main() -> io::Result<()> {
|
||||
app.refresh();
|
||||
last_refresh = Instant::now();
|
||||
app.bench_line = bench.status_line(app.cpus.len());
|
||||
if bench.running {
|
||||
if let Some((elapsed, _)) = bench.progress() {
|
||||
let dur = bench.duration.as_secs() as u32;
|
||||
if dur > 0 && elapsed >= dur {
|
||||
bench.stop();
|
||||
app.flash_toast(format!(
|
||||
"Benchmark done: {} {} in {}s",
|
||||
bench.last_score,
|
||||
bench.unit_name(),
|
||||
bench.last_duration_s
|
||||
));
|
||||
}
|
||||
if bench.running
|
||||
&& let Some((elapsed, _)) = bench.progress()
|
||||
{
|
||||
let dur = bench.duration.as_secs() as u32;
|
||||
if dur > 0 && elapsed >= dur {
|
||||
bench.stop();
|
||||
app.flash_toast(format!(
|
||||
"Benchmark done: {} {} in {}s",
|
||||
bench.last_score,
|
||||
bench.unit_name(),
|
||||
bench.last_duration_s
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(server) = dbus_server.as_ref() {
|
||||
@@ -605,29 +605,28 @@ fn main() -> io::Result<()> {
|
||||
if app.affinity_editor.open {
|
||||
f.render_widget(&mut app.affinity_editor, full);
|
||||
}
|
||||
if app.show_open_files {
|
||||
if let Some(files) = &app.open_files_result {
|
||||
let pid = app.open_files_for_pid.unwrap_or(0);
|
||||
let area =
|
||||
full.centered(Constraint::Percentage(70), Constraint::Percentage(60));
|
||||
f.render_widget(Clear, area);
|
||||
let lines: Vec<ratatui::text::Line> =
|
||||
crate::open_files::format_files(files)
|
||||
.into_iter()
|
||||
.map(ratatui::text::Line::from)
|
||||
.collect();
|
||||
let block = ratatui::widgets::Block::default()
|
||||
.borders(ratatui::widgets::Borders::ALL)
|
||||
.title(format!(
|
||||
" Open FDs: PID {} ({}) \u{2014} Esc to close ",
|
||||
pid,
|
||||
files.len()
|
||||
));
|
||||
let para = ratatui::widgets::Paragraph::new(lines)
|
||||
.block(block)
|
||||
.scroll((0, 0));
|
||||
f.render_widget(para, area);
|
||||
}
|
||||
if app.show_open_files
|
||||
&& let Some(files) = &app.open_files_result
|
||||
{
|
||||
let pid = app.open_files_for_pid.unwrap_or(0);
|
||||
let area =
|
||||
full.centered(Constraint::Percentage(70), Constraint::Percentage(60));
|
||||
f.render_widget(Clear, area);
|
||||
let lines: Vec<ratatui::text::Line> = crate::open_files::format_files(files)
|
||||
.into_iter()
|
||||
.map(ratatui::text::Line::from)
|
||||
.collect();
|
||||
let block = ratatui::widgets::Block::default()
|
||||
.borders(ratatui::widgets::Borders::ALL)
|
||||
.title(format!(
|
||||
" Open FDs: PID {} ({}) \u{2014} Esc to close ",
|
||||
pid,
|
||||
files.len()
|
||||
));
|
||||
let para = ratatui::widgets::Paragraph::new(lines)
|
||||
.block(block)
|
||||
.scroll((0, 0));
|
||||
f.render_widget(para, area);
|
||||
}
|
||||
// Hint bar: remind user how to exit expand mode.
|
||||
let hint = ratatui::widgets::Paragraph::new(ratatui::text::Line::styled(
|
||||
@@ -863,10 +862,8 @@ fn main() -> io::Result<()> {
|
||||
app.kill_dialog.close();
|
||||
app.flash_status("kill cancelled");
|
||||
}
|
||||
Key::Char('\n') if app.kill_dialog.open => {
|
||||
if app.kill_dialog.result.is_none() {
|
||||
app.kill_dialog.send_signal();
|
||||
}
|
||||
Key::Char('\n') if app.kill_dialog.open && app.kill_dialog.result.is_none() => {
|
||||
app.kill_dialog.send_signal();
|
||||
}
|
||||
Key::Up if app.kill_dialog.open => {
|
||||
app.kill_dialog.move_selection(-1);
|
||||
@@ -1000,17 +997,17 @@ fn main() -> io::Result<()> {
|
||||
});
|
||||
}
|
||||
Key::Char('+') => {
|
||||
let themes: [(&str, fn() -> crate::theme::Theme); 6] = [
|
||||
("dark", crate::theme::Theme::dark),
|
||||
("light", crate::theme::Theme::light),
|
||||
("high-contrast", crate::theme::Theme::high_contrast),
|
||||
("nord", crate::theme::Theme::nord),
|
||||
("gruvbox", crate::theme::Theme::gruvbox),
|
||||
("solarized-dark", crate::theme::Theme::solarized_dark),
|
||||
let themes: [(&str, crate::theme::Theme); 6] = [
|
||||
("dark", crate::theme::Theme::dark()),
|
||||
("light", crate::theme::Theme::light()),
|
||||
("high-contrast", crate::theme::Theme::high_contrast()),
|
||||
("nord", crate::theme::Theme::nord()),
|
||||
("gruvbox", crate::theme::Theme::gruvbox()),
|
||||
("solarized-dark", crate::theme::Theme::solarized_dark()),
|
||||
];
|
||||
app.theme_idx = (app.theme_idx + 1) % themes.len();
|
||||
let (name, ctor) = themes[app.theme_idx];
|
||||
app.theme = ctor();
|
||||
let (name, theme) = &themes[app.theme_idx];
|
||||
app.theme = theme.clone();
|
||||
app.flash_toast(format!("theme: {name}"));
|
||||
}
|
||||
Key::Char('k') if app.current_tab == TabId::Process => {
|
||||
@@ -1107,10 +1104,11 @@ fn main() -> io::Result<()> {
|
||||
app.flash_status("refresh interval (ms): type digits + Enter (50..60000)");
|
||||
}
|
||||
Key::Char(c) if interval_input.is_some() => {
|
||||
if let Some(buf) = interval_input.as_mut() {
|
||||
if c.is_ascii_digit() && buf.len() < 5 {
|
||||
buf.push(c);
|
||||
}
|
||||
if let Some(buf) = interval_input.as_mut()
|
||||
&& c.is_ascii_digit()
|
||||
&& buf.len() < 5
|
||||
{
|
||||
buf.push(c);
|
||||
}
|
||||
}
|
||||
Key::Backspace if interval_input.is_some() => {
|
||||
@@ -1256,20 +1254,20 @@ fn main() -> io::Result<()> {
|
||||
"process filter: type chars + Enter to apply, Esc to clear",
|
||||
);
|
||||
}
|
||||
Key::Char(c) if process_filter_input.is_some() && !interval_input.is_some() => {
|
||||
Key::Char(c) if process_filter_input.is_some() && interval_input.is_none() => {
|
||||
if let Some(buf) = process_filter_input.as_mut() {
|
||||
buf.push(c);
|
||||
}
|
||||
}
|
||||
Key::Backspace
|
||||
if process_filter_input.is_some() && !interval_input.is_some() =>
|
||||
if process_filter_input.is_some() && interval_input.is_none() =>
|
||||
{
|
||||
if let Some(buf) = process_filter_input.as_mut() {
|
||||
buf.pop();
|
||||
}
|
||||
}
|
||||
Key::Char('\n')
|
||||
if process_filter_input.is_some() && !interval_input.is_some() =>
|
||||
if process_filter_input.is_some() && interval_input.is_none() =>
|
||||
{
|
||||
let new_filter = process_filter_input.take().unwrap_or_default();
|
||||
app.process_filter = new_filter.clone();
|
||||
|
||||
@@ -68,18 +68,18 @@ impl MemInfo {
|
||||
pub fn read_meminfo() -> MemInfo {
|
||||
let mut info = MemInfo::default();
|
||||
// Redox scheme: a single file with key:value lines (analogous to /proc/meminfo).
|
||||
if let Ok(s) = fs::read_to_string("/scheme/sys/mem") {
|
||||
if parse_meminfo_kv(&s, &mut info) {
|
||||
info.available = true;
|
||||
return info;
|
||||
}
|
||||
if let Ok(s) = fs::read_to_string("/scheme/sys/mem")
|
||||
&& parse_meminfo_kv(&s, &mut info)
|
||||
{
|
||||
info.available = true;
|
||||
return info;
|
||||
}
|
||||
// Linux /proc/meminfo.
|
||||
if let Ok(s) = fs::read_to_string("/proc/meminfo") {
|
||||
if parse_meminfo_kv(&s, &mut info) {
|
||||
info.available = true;
|
||||
return info;
|
||||
}
|
||||
if let Ok(s) = fs::read_to_string("/proc/meminfo")
|
||||
&& parse_meminfo_kv(&s, &mut info)
|
||||
{
|
||||
info.available = true;
|
||||
return info;
|
||||
}
|
||||
info
|
||||
}
|
||||
@@ -171,15 +171,15 @@ pub fn read_os_info() -> OsInfo {
|
||||
}
|
||||
}
|
||||
// Redox fallback: use uname release string as the name (no /etc/os-release).
|
||||
if info.name.is_empty() {
|
||||
if let Ok(s) = fs::read_to_string("/scheme/sys/uname") {
|
||||
for line in s.lines() {
|
||||
if let Some(rest) = line.strip_prefix("release=") {
|
||||
let v = rest.trim().to_string();
|
||||
if !v.is_empty() {
|
||||
info.name = v;
|
||||
break;
|
||||
}
|
||||
if info.name.is_empty()
|
||||
&& let Ok(s) = fs::read_to_string("/scheme/sys/uname")
|
||||
{
|
||||
for line in s.lines() {
|
||||
if let Some(rest) = line.strip_prefix("release=") {
|
||||
let v = rest.trim().to_string();
|
||||
if !v.is_empty() {
|
||||
info.name = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -214,12 +214,11 @@ pub fn read_os_info() -> OsInfo {
|
||||
}
|
||||
|
||||
// Uptime: /proc/uptime first field is seconds (float).
|
||||
if let Ok(s) = fs::read_to_string("/proc/uptime") {
|
||||
if let Some(first) = s.split_whitespace().next() {
|
||||
if let Ok(v) = first.parse::<f64>() {
|
||||
info.uptime_secs = v as u64;
|
||||
}
|
||||
}
|
||||
if let Ok(s) = fs::read_to_string("/proc/uptime")
|
||||
&& let Some(first) = s.split_whitespace().next()
|
||||
&& let Ok(v) = first.parse::<f64>()
|
||||
{
|
||||
info.uptime_secs = v as u64;
|
||||
}
|
||||
|
||||
info.available = !info.kernel.is_empty() || !info.name.is_empty() || info.uptime_secs > 0;
|
||||
|
||||
@@ -171,10 +171,10 @@ pub fn read_msr(cpu: u32, msr: u32) -> Option<u64> {
|
||||
let scheme_path = format!("/scheme/sys/msr/{}/0x{:x}", cpu, msr);
|
||||
if Path::new(&scheme_path).exists() {
|
||||
let mut data = [0u8; 8];
|
||||
if let Ok(mut f) = fs::File::open(&scheme_path) {
|
||||
if f.read_exact(&mut data).is_ok() {
|
||||
return Some(u64::from_le_bytes(data));
|
||||
}
|
||||
if let Ok(mut f) = fs::File::open(&scheme_path)
|
||||
&& f.read_exact(&mut data).is_ok()
|
||||
{
|
||||
return Some(u64::from_le_bytes(data));
|
||||
}
|
||||
// Path exists but open/read failed — permission denied or similar.
|
||||
mark_msr_failed(cpu);
|
||||
@@ -183,12 +183,12 @@ pub fn read_msr(cpu: u32, msr: u32) -> Option<u64> {
|
||||
// Linux fallback: /dev/cpu/{cpu}/msr
|
||||
let dev_path = format!("/dev/cpu/{}/msr", cpu);
|
||||
if Path::new(&dev_path).exists() {
|
||||
if let Ok(mut f) = fs::File::open(&dev_path) {
|
||||
if f.seek(SeekFrom::Start(msr as u64)).is_ok() {
|
||||
let mut data = [0u8; 8];
|
||||
if f.read_exact(&mut data).is_ok() {
|
||||
return Some(u64::from_le_bytes(data));
|
||||
}
|
||||
if let Ok(mut f) = fs::File::open(&dev_path)
|
||||
&& f.seek(SeekFrom::Start(msr as u64)).is_ok()
|
||||
{
|
||||
let mut data = [0u8; 8];
|
||||
if f.read_exact(&mut data).is_ok() {
|
||||
return Some(u64::from_le_bytes(data));
|
||||
}
|
||||
}
|
||||
// Dev path exists but open/read failed.
|
||||
@@ -201,14 +201,14 @@ pub fn read_msr(cpu: u32, msr: u32) -> Option<u64> {
|
||||
pub fn write_msr(cpu: u32, msr: u32, val: u64) -> bool {
|
||||
let scheme_path = format!("/scheme/sys/msr/{}/0x{:x}", cpu, msr);
|
||||
if Path::new(&scheme_path).exists() {
|
||||
return fs::write(&scheme_path, &val.to_le_bytes()).is_ok();
|
||||
return fs::write(&scheme_path, val.to_le_bytes()).is_ok();
|
||||
}
|
||||
let dev_path = format!("/dev/cpu/{}/msr", cpu);
|
||||
if let Ok(mut f) = fs::OpenOptions::new().write(true).open(&dev_path) {
|
||||
if f.seek(SeekFrom::Start(msr as u64)).is_ok() {
|
||||
use std::io::Write;
|
||||
return f.write_all(&val.to_le_bytes()).is_ok();
|
||||
}
|
||||
if let Ok(mut f) = fs::OpenOptions::new().write(true).open(&dev_path)
|
||||
&& f.seek(SeekFrom::Start(msr as u64)).is_ok()
|
||||
{
|
||||
use std::io::Write;
|
||||
return f.write_all(&val.to_le_bytes()).is_ok();
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use crate::theme;
|
||||
const MIN_NICE: i32 = -20;
|
||||
const MAX_NICE: i32 = 19;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct NiceEdit {
|
||||
pub open: bool,
|
||||
pub pid: u32,
|
||||
@@ -19,24 +20,11 @@ pub struct NiceEdit {
|
||||
pub result_timer: Option<std::time::Instant>,
|
||||
}
|
||||
|
||||
impl Default for NiceEdit {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
open: false,
|
||||
pid: 0,
|
||||
comm: String::new(),
|
||||
value: 0,
|
||||
result: None,
|
||||
result_timer: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NiceEdit {
|
||||
pub fn open_for(&mut self, pid: u32, comm: &str, current_nice: i32) {
|
||||
self.pid = pid;
|
||||
self.comm = comm.to_string();
|
||||
self.value = current_nice.clamp(MIN_NICE, MAX_NICE) as i32;
|
||||
self.value = current_nice.clamp(MIN_NICE, MAX_NICE);
|
||||
self.open = true;
|
||||
self.result = None;
|
||||
self.result_timer = None;
|
||||
@@ -59,10 +47,10 @@ impl NiceEdit {
|
||||
}
|
||||
|
||||
pub fn auto_close_if_done(&mut self) {
|
||||
if let Some(t) = self.result_timer {
|
||||
if t.elapsed().as_secs() >= 2 {
|
||||
self.close();
|
||||
}
|
||||
if let Some(t) = self.result_timer
|
||||
&& t.elapsed().as_secs() >= 2
|
||||
{
|
||||
self.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,7 +119,7 @@ impl Widget for &NiceEdit {
|
||||
Span::raw("]"),
|
||||
]);
|
||||
Widget::render(
|
||||
&Paragraph::new(slider_line).alignment(Alignment::Center),
|
||||
Paragraph::new(slider_line).alignment(Alignment::Center),
|
||||
slider_area,
|
||||
buf,
|
||||
);
|
||||
@@ -150,6 +138,6 @@ impl Widget for &NiceEdit {
|
||||
theme::VALUE_OFF,
|
||||
))
|
||||
};
|
||||
Widget::render(&Paragraph::new(msg), msg_area, buf);
|
||||
Widget::render(Paragraph::new(msg), msg_area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ impl SortMode {
|
||||
pub fn sort_ascending(self, processes: &mut Vec<ProcessInfo>, ascending: bool) {
|
||||
if ascending {
|
||||
match self {
|
||||
SortMode::Rss => processes.sort_by(|a, b| a.rss_kb.cmp(&b.rss_kb)),
|
||||
SortMode::Rss => processes.sort_by_key(|a| a.rss_kb),
|
||||
SortMode::Cpu => processes.sort_by(|a, b| {
|
||||
a.cpu_pct
|
||||
.partial_cmp(&b.cpu_pct)
|
||||
@@ -132,9 +132,9 @@ impl SortMode {
|
||||
SortMode::IoWriteRate => {
|
||||
sort_by_io_rate_field_asc(processes, |p| p.io_write_rate_kbs)
|
||||
}
|
||||
SortMode::RChar => processes.sort_by(|a, b| a.io_rchar_kb.cmp(&b.io_rchar_kb)),
|
||||
SortMode::WChar => processes.sort_by(|a, b| a.io_wchar_kb.cmp(&b.io_wchar_kb)),
|
||||
SortMode::VSize => processes.sort_by(|a, b| a.vsize_kb.cmp(&b.vsize_kb)),
|
||||
SortMode::RChar => processes.sort_by_key(|a| a.io_rchar_kb),
|
||||
SortMode::WChar => processes.sort_by_key(|a| a.io_wchar_kb),
|
||||
SortMode::VSize => processes.sort_by_key(|a| a.vsize_kb),
|
||||
SortMode::Pid => processes.sort_by_key(|p| p.pid),
|
||||
SortMode::Name => processes.sort_by(|a, b| a.comm.cmp(&b.comm)),
|
||||
SortMode::ThreadIo => sort_by_io_field_asc(processes, |p| {
|
||||
@@ -308,7 +308,7 @@ pub fn sort_tree(processes: &mut Vec<ProcessInfo>, sort_mode: SortMode) {
|
||||
let mut out: Vec<ProcessInfo> = Vec::with_capacity(processes.len());
|
||||
let mut visited: BTreeSet<u32> = BTreeSet::new();
|
||||
for &root in &roots {
|
||||
dfs_emit(&processes, &children, root, &mut out, &mut visited);
|
||||
dfs_emit(processes, &children, root, &mut out, &mut visited);
|
||||
}
|
||||
|
||||
// 5. Append any leftover procs (defensive — should not happen
|
||||
@@ -637,11 +637,11 @@ fn read_thread_io(pid: u32) -> (Option<u64>, Option<u64>) {
|
||||
total_read = total_read.saturating_add(v);
|
||||
any_counted = true;
|
||||
}
|
||||
} else if let Some(rest) = line.strip_prefix("write_bytes:") {
|
||||
if let Ok(v) = rest.trim().parse::<u64>() {
|
||||
total_write = total_write.saturating_add(v);
|
||||
any_counted = true;
|
||||
}
|
||||
} else if let Some(rest) = line.strip_prefix("write_bytes:")
|
||||
&& let Ok(v) = rest.trim().parse::<u64>()
|
||||
{
|
||||
total_write = total_write.saturating_add(v);
|
||||
any_counted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -721,10 +721,10 @@ pub fn parse_cpu_list(s: &str) -> Vec<u32> {
|
||||
out.push(c);
|
||||
}
|
||||
}
|
||||
} else if let Ok(n) = chunk.parse::<u32>() {
|
||||
if !out.contains(&n) {
|
||||
out.push(n);
|
||||
}
|
||||
} else if let Ok(n) = chunk.parse::<u32>()
|
||||
&& !out.contains(&n)
|
||||
{
|
||||
out.push(n);
|
||||
}
|
||||
}
|
||||
out.sort_unstable();
|
||||
@@ -863,20 +863,20 @@ fn read_process(pid: u32) -> Option<ProcessInfo> {
|
||||
}
|
||||
|
||||
fn derive_sched_policy(pid: u32, priority: i64) -> String {
|
||||
if let Ok(data) = fs::read(format!("/proc/{}/sched-policy", pid)) {
|
||||
if let Some(&policy) = data.first() {
|
||||
return match policy {
|
||||
0 => "FIFO",
|
||||
1 => "RR",
|
||||
2 => "OTHER",
|
||||
_ => "?",
|
||||
}
|
||||
.into();
|
||||
if let Ok(data) = fs::read(format!("/proc/{}/sched-policy", pid))
|
||||
&& let Some(&policy) = data.first()
|
||||
{
|
||||
return match policy {
|
||||
0 => "FIFO",
|
||||
1 => "RR",
|
||||
2 => "OTHER",
|
||||
_ => "?",
|
||||
}
|
||||
.into();
|
||||
}
|
||||
if priority > 0 && priority < 40 {
|
||||
"OTHER".into()
|
||||
} else if priority >= 40 && priority < 100 {
|
||||
} else if (40..100).contains(&priority) {
|
||||
"RT".into()
|
||||
} else {
|
||||
"OTHER".into()
|
||||
@@ -2005,7 +2005,7 @@ pub fn set_nice(pid: u32, nice: i32) -> Result<(), String> {
|
||||
#[cfg(target_os = "linux")]
|
||||
unsafe {
|
||||
// PRIO_PROCESS = 0, who = pid
|
||||
if libc::setpriority(0, pid as u32, n) == 0 {
|
||||
if libc::setpriority(0, pid, n) == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(std::io::Error::last_os_error().to_string())
|
||||
|
||||
@@ -186,11 +186,9 @@ pub fn render_header<'a>(app: &'a App, focused: bool) -> Paragraph<'a> {
|
||||
" ".into(),
|
||||
"Throttle: ".set_style(theme::LABEL),
|
||||
match app.throttle {
|
||||
ThrottleMode::Auto => "AUTO".set_style(theme::HEADER_THROTTLE_AUTO).into(),
|
||||
ThrottleMode::User => "USER".set_style(theme::HEADER_THROTTLE_USER).into(),
|
||||
ThrottleMode::ForcedMin => {
|
||||
"FORCED MIN".set_style(theme::HEADER_THROTTLE_FORCED).into()
|
||||
}
|
||||
ThrottleMode::Auto => "AUTO".set_style(theme::HEADER_THROTTLE_AUTO),
|
||||
ThrottleMode::User => "USER".set_style(theme::HEADER_THROTTLE_USER),
|
||||
ThrottleMode::ForcedMin => "FORCED MIN".set_style(theme::HEADER_THROTTLE_FORCED),
|
||||
},
|
||||
]),
|
||||
Line::from(vec![
|
||||
@@ -986,13 +984,14 @@ pub fn render_network_panel<'a>(app: &'a App, focused: bool) -> Paragraph<'a> {
|
||||
.unwrap_or("?")
|
||||
.set_style(theme::VALUE),
|
||||
]));
|
||||
if let Some(mac) = &iface.mac_address {
|
||||
if !mac.is_empty() && mac != "00:00:00:00:00:00" {
|
||||
lines.push(Line::from(vec![
|
||||
" MAC: ".set_style(theme::LABEL),
|
||||
mac.clone().set_style(theme::VALUE),
|
||||
]));
|
||||
}
|
||||
if let Some(mac) = &iface.mac_address
|
||||
&& !mac.is_empty()
|
||||
&& mac != "00:00:00:00:00:00"
|
||||
{
|
||||
lines.push(Line::from(vec![
|
||||
" MAC: ".set_style(theme::LABEL),
|
||||
mac.clone().set_style(theme::VALUE),
|
||||
]));
|
||||
}
|
||||
if let Some(mtu) = iface.mtu {
|
||||
lines.push(Line::from(vec![
|
||||
@@ -1000,13 +999,13 @@ pub fn render_network_panel<'a>(app: &'a App, focused: bool) -> Paragraph<'a> {
|
||||
mtu.to_string().set_style(theme::VALUE),
|
||||
]));
|
||||
}
|
||||
if let Some(speed) = iface.speed_mbps {
|
||||
if speed > 0 {
|
||||
lines.push(Line::from(vec![
|
||||
" Speed: ".set_style(theme::LABEL),
|
||||
format!("{} Mbps", speed).set_style(theme::VALUE),
|
||||
]));
|
||||
}
|
||||
if let Some(speed) = iface.speed_mbps
|
||||
&& speed > 0
|
||||
{
|
||||
lines.push(Line::from(vec![
|
||||
" Speed: ".set_style(theme::LABEL),
|
||||
format!("{} Mbps", speed).set_style(theme::VALUE),
|
||||
]));
|
||||
}
|
||||
lines.push(Line::from(vec![
|
||||
" RX bytes: ".set_style(theme::LABEL),
|
||||
@@ -1481,7 +1480,7 @@ fn tree_prefix(
|
||||
None => return String::new(),
|
||||
};
|
||||
let next_ppid = all.get(my_index + 1).map(|n| n.ppid);
|
||||
let is_last = next_ppid.map_or(true, |np| np != ppid);
|
||||
let is_last = next_ppid != Some(ppid);
|
||||
|
||||
// For each ancestor level, decide if the bar continues. The
|
||||
// bar at depth d (counting from 0 for the row's immediate
|
||||
@@ -1494,7 +1493,7 @@ fn tree_prefix(
|
||||
let mut bar = String::new();
|
||||
for &ancestor in ancestors.iter() {
|
||||
let next_row = all.get(my_index + 1);
|
||||
let ancestor_active_in_next = next_row.map_or(false, |n| {
|
||||
let ancestor_active_in_next = next_row.is_some_and(|n| {
|
||||
if n.ppid == ancestor {
|
||||
return true;
|
||||
}
|
||||
@@ -1910,7 +1909,7 @@ pub fn render_cpu_table<'a>(
|
||||
let mut padded: Vec<u8> = cpu.load_history.iter().copied().collect();
|
||||
if padded.len() < SPARK_WIDTH {
|
||||
let pad = SPARK_WIDTH - padded.len();
|
||||
padded = std::iter::repeat(0u8).take(pad).chain(padded).collect();
|
||||
padded = std::iter::repeat_n(0u8, pad).chain(padded).collect();
|
||||
}
|
||||
padded_to_sparkline(&padded)
|
||||
};
|
||||
@@ -1960,76 +1959,76 @@ pub fn render_cpu_table<'a>(
|
||||
})
|
||||
.collect();
|
||||
let mut rows = rows;
|
||||
if let Some(expanded_id) = expanded_cpu {
|
||||
if let Some(cpu) = cpus.iter().find(|c| c.id == expanded_id) {
|
||||
let sub_style = theme::LABEL;
|
||||
let active_style = Style::new().yellow().bold();
|
||||
for (idx, pstate) in cpu.pstates.iter().enumerate() {
|
||||
let is_current = cpu.current_idx == Some(idx);
|
||||
let s = if is_current { active_style } else { sub_style };
|
||||
let marker = if is_current { "▶" } else { "↳" };
|
||||
let label = if is_current {
|
||||
format!("{marker} P{idx} (current)")
|
||||
} else {
|
||||
format!("{marker} P{idx}")
|
||||
};
|
||||
let freq_mhz = pstate.freq_khz / 1000;
|
||||
let power_w = pstate.power_mw as f64 / 1000.0;
|
||||
rows.push(Row::new(vec![
|
||||
label.set_style(s),
|
||||
format!("{freq_mhz} MHz").set_style(s),
|
||||
format!("{power_w:.1} W").set_style(s),
|
||||
format!("0x{:02x}", (pstate.ctl >> 8) & 0x7f).set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
]));
|
||||
}
|
||||
if let Some(ref hwp) = cpu.hwp {
|
||||
let s = sub_style;
|
||||
rows.push(Row::new(vec![
|
||||
" HWP caps".set_style(s),
|
||||
format!(
|
||||
"eff={} guar={} max={}",
|
||||
hwp.efficient_perf, hwp.guaranteed_perf, hwp.max_perf
|
||||
)
|
||||
.set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
format!("{}", if hwp.enabled { "ON" } else { "OFF" }).set_style(
|
||||
if hwp.enabled {
|
||||
Style::new().green()
|
||||
} else {
|
||||
theme::VALUE_OFF
|
||||
},
|
||||
),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
]));
|
||||
rows.push(Row::new(vec![
|
||||
" HWP req".set_style(s),
|
||||
format!(
|
||||
"min={} max={} des={} EPP={} ({})",
|
||||
hwp.min_request,
|
||||
hwp.max_request,
|
||||
hwp.desired_perf,
|
||||
hwp.epp,
|
||||
hwp.epp_label()
|
||||
)
|
||||
.set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
]));
|
||||
}
|
||||
if let Some(expanded_id) = expanded_cpu
|
||||
&& let Some(cpu) = cpus.iter().find(|c| c.id == expanded_id)
|
||||
{
|
||||
let sub_style = theme::LABEL;
|
||||
let active_style = Style::new().yellow().bold();
|
||||
for (idx, pstate) in cpu.pstates.iter().enumerate() {
|
||||
let is_current = cpu.current_idx == Some(idx);
|
||||
let s = if is_current { active_style } else { sub_style };
|
||||
let marker = if is_current { "▶" } else { "↳" };
|
||||
let label = if is_current {
|
||||
format!("{marker} P{idx} (current)")
|
||||
} else {
|
||||
format!("{marker} P{idx}")
|
||||
};
|
||||
let freq_mhz = pstate.freq_khz / 1000;
|
||||
let power_w = pstate.power_mw as f64 / 1000.0;
|
||||
rows.push(Row::new(vec![
|
||||
label.set_style(s),
|
||||
format!("{freq_mhz} MHz").set_style(s),
|
||||
format!("{power_w:.1} W").set_style(s),
|
||||
format!("0x{:02x}", (pstate.ctl >> 8) & 0x7f).set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
]));
|
||||
}
|
||||
if let Some(ref hwp) = cpu.hwp {
|
||||
let s = sub_style;
|
||||
rows.push(Row::new(vec![
|
||||
" HWP caps".set_style(s),
|
||||
format!(
|
||||
"eff={} guar={} max={}",
|
||||
hwp.efficient_perf, hwp.guaranteed_perf, hwp.max_perf
|
||||
)
|
||||
.set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
(if hwp.enabled { "ON" } else { "OFF" })
|
||||
.to_string()
|
||||
.set_style(if hwp.enabled {
|
||||
Style::new().green()
|
||||
} else {
|
||||
theme::VALUE_OFF
|
||||
}),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
]));
|
||||
rows.push(Row::new(vec![
|
||||
" HWP req".set_style(s),
|
||||
format!(
|
||||
"min={} max={} des={} EPP={} ({})",
|
||||
hwp.min_request,
|
||||
hwp.max_request,
|
||||
hwp.desired_perf,
|
||||
hwp.epp,
|
||||
hwp.epp_label()
|
||||
)
|
||||
.set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
"".set_style(s),
|
||||
]));
|
||||
}
|
||||
}
|
||||
Table::new(
|
||||
|
||||
@@ -113,10 +113,10 @@ impl SchedStats {
|
||||
stats.context_switches = rest.trim().parse().ok();
|
||||
}
|
||||
// "intr <total> <irq0> <irq1> ..." — first field is total.
|
||||
if let Some(rest) = trimmed.strip_prefix("intr ") {
|
||||
if let Some(total_str) = rest.split_whitespace().next() {
|
||||
stats.total_irqs = total_str.parse().ok();
|
||||
}
|
||||
if let Some(rest) = trimmed.strip_prefix("intr ")
|
||||
&& let Some(total_str) = rest.split_whitespace().next()
|
||||
{
|
||||
stats.total_irqs = total_str.parse().ok();
|
||||
}
|
||||
}
|
||||
stats
|
||||
|
||||
@@ -88,7 +88,7 @@ impl SmartInfo {
|
||||
|
||||
fn read_smart_for_disk(disk: &str) -> SmartHealth {
|
||||
let output = Command::new("smartctl")
|
||||
.args(["-A", "-H", "/dev/", &format!("{disk}").to_string()])
|
||||
.args(["-A", "-H", "/dev/", &disk.to_string().to_string()])
|
||||
.output();
|
||||
let output = match output {
|
||||
Ok(o) => o,
|
||||
|
||||
@@ -42,7 +42,7 @@ impl DiskStats {
|
||||
Self {
|
||||
read_bytes: fields.get(2).copied().unwrap_or(0),
|
||||
write_bytes: fields.get(6).copied().unwrap_or(0),
|
||||
reads_completed: fields.get(0).copied().unwrap_or(0),
|
||||
reads_completed: fields.first().copied().unwrap_or(0),
|
||||
writes_completed: fields.get(4).copied().unwrap_or(0),
|
||||
read_kbps: 0.0,
|
||||
write_kbps: 0.0,
|
||||
@@ -97,10 +97,12 @@ fn read_disk(name: &str, path: &Path) -> Option<DiskInfo> {
|
||||
if let Ok(entries) = fs::read_dir(path) {
|
||||
for entry in entries.flatten() {
|
||||
let p = entry.path();
|
||||
if let Some(n) = p.file_name().and_then(|s| s.to_str()) {
|
||||
if p.is_dir() && n.starts_with(name) && n != name {
|
||||
partitions.push(n.to_string());
|
||||
}
|
||||
if let Some(n) = p.file_name().and_then(|s| s.to_str())
|
||||
&& p.is_dir()
|
||||
&& n.starts_with(name)
|
||||
&& n != name
|
||||
{
|
||||
partitions.push(n.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user