bug/refactor: draw average CPU last, refactor CPU data code (#1804)

This PR makes it so we draw the average CPU on top again. This also refactors internals to have the average CPU stored separately.
This commit is contained in:
Clement Tsang 2025-08-22 00:19:03 -04:00 committed by GitHub
parent 58a9a643a4
commit 43e1b34899
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 102 additions and 58 deletions

View File

@ -29,6 +29,7 @@ That said, these are more guidelines rather than hardset rules, though the proje
### Bug Fixes
- [#1800](https://github.com/ClementTsang/bottom/pull/1800): Fix colon at end of process name in Linux.
- [#1804](https://github.com/ClementTsang/bottom/pull/1804): Draw average CPU last again.
## [0.11.1] - 2025-08-15

View File

@ -30,7 +30,7 @@ pub struct StoredData {
pub arc_harvest: Option<MemData>,
#[cfg(feature = "gpu")]
pub gpu_harvest: Vec<(String, MemData)>,
pub cpu_harvest: cpu::CpuHarvest,
pub cpu_harvest: Vec<cpu::CpuData>,
pub load_avg_harvest: cpu::LoadAvgHarvest,
pub process_data: ProcessData,
/// TODO: (points_rework_v1) Might be a better way to do this without having to store here?
@ -51,7 +51,7 @@ impl Default for StoredData {
#[cfg(not(target_os = "windows"))]
cache_harvest: None,
swap_harvest: None,
cpu_harvest: cpu::CpuHarvest::default(),
cpu_harvest: Default::default(),
load_avg_harvest: cpu::LoadAvgHarvest::default(),
process_data: Default::default(),
prev_io: Vec::default(),
@ -114,7 +114,25 @@ impl StoredData {
}
if let Some(cpu) = data.cpu {
self.cpu_harvest = cpu;
self.cpu_harvest.clear();
if let Some(avg) = cpu.avg {
self.cpu_harvest.push(cpu::CpuData {
data_type: cpu::CpuDataType::Avg,
usage: avg,
});
}
self.cpu_harvest
.extend(
cpu.cpus
.into_iter()
.enumerate()
.map(|(core, usage)| cpu::CpuData {
data_type: cpu::CpuDataType::Cpu(core as u32),
usage,
}),
);
}
if let Some(load_avg) = data.load_avg {

View File

@ -38,6 +38,9 @@ pub struct TimeSeriesData {
/// CPU data.
pub cpu: Vec<Values>,
/// Average CPU data.
pub avg_cpu: Values,
/// RAM memory data.
pub ram: Values,
@ -70,10 +73,12 @@ impl TimeSeriesData {
self.tx.insert_break();
}
if let Some(cpu) = &data.cpu {
match self.cpu.len().cmp(&cpu.len()) {
if let Some(cpu_harvest) = &data.cpu {
let cpus = &cpu_harvest.cpus;
match self.cpu.len().cmp(&cpus.len()) {
Ordering::Less => {
let diff = cpu.len() - self.cpu.len();
let diff = cpus.len() - self.cpu.len();
self.cpu.reserve_exact(diff);
for _ in 0..diff {
@ -81,7 +86,7 @@ impl TimeSeriesData {
}
}
Ordering::Greater => {
let diff = self.cpu.len() - cpu.len();
let diff = self.cpu.len() - cpus.len();
let offset = self.cpu.len() - diff;
for curr in &mut self.cpu[offset..] {
@ -91,13 +96,20 @@ impl TimeSeriesData {
Ordering::Equal => {}
}
for (curr, new_data) in self.cpu.iter_mut().zip(cpu.iter()) {
curr.push(new_data.usage.into());
for (curr, new_data) in self.cpu.iter_mut().zip(cpus.iter()) {
curr.push((*new_data).into());
}
// If there isn't avg then we never had any to begin with.
if let Some(avg) = cpu_harvest.avg {
self.avg_cpu.push(avg.into());
}
} else {
for c in &mut self.cpu {
c.insert_break();
}
self.avg_cpu.insert_break();
}
if let Some(memory) = &data.memory {

View File

@ -149,7 +149,8 @@ impl Painter {
CpuDataType::Avg => ("AVG".to_string(), self.styles.avg_cpu_colour),
CpuDataType::Cpu(index) => (
format!("{index:<3}",),
self.styles.cpu_colour_styles[index % self.styles.cpu_colour_styles.len()],
self.styles.cpu_colour_styles
[(index as usize) % self.styles.cpu_colour_styles.len()],
),
};

View File

@ -119,7 +119,6 @@ impl Painter {
fn generate_points<'a>(
&self, cpu_widget_state: &'a CpuWidgetState, data: &'a StoredData, show_avg_cpu: bool,
) -> Vec<GraphData<'a>> {
let show_avg_offset = if show_avg_cpu { AVG_POSITION } else { 0 };
let current_scroll_position = cpu_widget_state.table.state.current_index;
let cpu_entries = &data.cpu_harvest;
let cpu_points = &data.timeseries_data.cpu;
@ -128,39 +127,59 @@ impl Painter {
if current_scroll_position == ALL_POSITION {
// This case ensures the other cases cannot have the position be equal to 0.
cpu_points
.iter()
.enumerate()
.map(|(itx, values)| {
let style = if show_avg_cpu && itx == AVG_POSITION {
self.styles.avg_cpu_colour
} else if itx == ALL_POSITION {
self.styles.all_cpu_colour
} else {
self.styles.cpu_colour_styles
[(itx - show_avg_offset) % self.styles.cpu_colour_styles.len()]
};
let capacity = if show_avg_cpu {
cpu_points.len() + 1
} else {
cpu_points.len()
};
let mut points = Vec::with_capacity(capacity);
GraphData::default().style(style).time(time).values(values)
})
.collect()
points.extend(cpu_points.iter().enumerate().map(|(itx, values)| {
let style_index = itx % self.styles.cpu_colour_styles.len();
let style = self.styles.cpu_colour_styles[style_index];
GraphData::default().style(style).time(time).values(values)
}));
// We draw avg last so it is drawn on top.
if show_avg_cpu {
points.push(
GraphData::default()
.style(self.styles.avg_cpu_colour)
.time(time)
.values(&data.timeseries_data.avg_cpu),
);
}
points
} else if let Some(CpuData { .. }) = cpu_entries.get(current_scroll_position - 1) {
// We generally subtract one from current scroll position because of the all entry.
let style = if show_avg_cpu && current_scroll_position == AVG_POSITION {
let show_avg_offset = if show_avg_cpu { AVG_POSITION } else { 0 };
let is_avg = show_avg_cpu && current_scroll_position == AVG_POSITION;
let style = if is_avg {
self.styles.avg_cpu_colour
} else {
let offset_position = current_scroll_position - 1;
self.styles.cpu_colour_styles
[(offset_position - show_avg_offset) % self.styles.cpu_colour_styles.len()]
self.styles.cpu_colour_styles[(current_scroll_position - 1 - show_avg_offset)
% self.styles.cpu_colour_styles.len()]
};
vec![
GraphData::default()
.style(style)
.time(time)
.values(&cpu_points[current_scroll_position - 1]),
]
if is_avg {
vec![
GraphData::default()
.style(style)
.time(time)
.values(&data.timeseries_data.avg_cpu),
]
} else {
vec![
GraphData::default()
.style(style)
.time(time)
.values(&cpu_points[current_scroll_position - 1 - show_avg_offset]),
]
}
} else {
vec![]
}

View File

@ -8,7 +8,7 @@ pub type LoadAvgHarvest = [f32; 3];
#[derive(Debug, Clone, Copy)]
pub enum CpuDataType {
Avg,
Cpu(usize),
Cpu(u32),
}
#[derive(Debug, Clone)]
@ -17,4 +17,8 @@ pub struct CpuData {
pub usage: f32,
}
pub type CpuHarvest = Vec<CpuData>;
#[derive(Debug, Clone, Default)]
pub struct CpuHarvest {
pub avg: Option<f32>,
pub cpus: Vec<f32>,
}

View File

@ -3,31 +3,19 @@
use sysinfo::System;
use super::{CpuData, CpuDataType, CpuHarvest};
use super::CpuHarvest;
use crate::collection::error::CollectionResult;
pub fn get_cpu_data_list(sys: &System, show_average_cpu: bool) -> CollectionResult<CpuHarvest> {
let mut cpus = vec![];
let avg = show_average_cpu.then(|| sys.global_cpu_usage());
if show_average_cpu {
cpus.push(CpuData {
data_type: CpuDataType::Avg,
usage: sys.global_cpu_usage(),
})
}
let cpus = sys
.cpus()
.iter()
.map(|cpu| cpu.cpu_usage())
.collect::<Vec<_>>();
cpus.extend(
sys.cpus()
.iter()
.enumerate()
.map(|(i, cpu)| CpuData {
data_type: CpuDataType::Cpu(i),
usage: cpu.cpu_usage(),
})
.collect::<Vec<_>>(),
);
Ok(cpus)
Ok(CpuHarvest { avg, cpus })
}
#[cfg(target_family = "unix")]

View File

@ -102,6 +102,7 @@ impl DataToCell<CpuWidgetColumn> for CpuWidgetTableData {
} => match data_type {
CpuDataType::Avg => painter.styles.avg_cpu_colour,
CpuDataType::Cpu(index) => {
let index = *index as usize;
painter.styles.cpu_colour_styles[index % painter.styles.cpu_colour_styles.len()]
}
},