From 98b7a8d201942f904f1aa9af40669445cab13157 Mon Sep 17 00:00:00 2001 From: electria Date: Sat, 15 Aug 2026 18:16:16 -0700 Subject: [PATCH] feat: multiple graphs --- src/app/cpu_graph.rs | 19 ++++++++++++- src/app/mod.rs | 60 ++++++++++++++++++++++++++++++----------- src/main.rs | 1 + src/sysinfo_snapshot.rs | 18 +++++++++++++ 4 files changed, 82 insertions(+), 16 deletions(-) create mode 100644 src/sysinfo_snapshot.rs diff --git a/src/app/cpu_graph.rs b/src/app/cpu_graph.rs index 42a4a22..9fe439c 100644 --- a/src/app/cpu_graph.rs +++ b/src/app/cpu_graph.rs @@ -8,11 +8,23 @@ const CYAN: Color = Color::from_rgb8(0, 255, 255); #[derive(Default)] pub struct CpuGraph { - pub cache: canvas::Cache, timeline: VecDeque, + + pub cache: canvas::Cache, } impl CpuGraph { + pub fn new(usage: f32) -> Self { + Self { + timeline: { + let mut vec = VecDeque::new(); + vec.push_front(usage); + vec + }, + ..Default::default() + } + } + pub fn update(&mut self, usage: f32) { self.timeline.push_back(usage); if self.timeline.len() > 1000 { @@ -32,6 +44,11 @@ impl canvas::Program for CpuGraph { _cursor: mouse::Cursor, ) -> Vec> { let geometry = self.cache.draw(renderer, bounds.size(), |frame| { + frame.stroke_rectangle( + Point::new(0., 0.), + bounds.size(), + canvas::Stroke::default().with_color(Color::from_rgb8(255, 0, 0)), + ); frame.stroke( &canvas::Path::new(|builder| { for (index, value) in self.timeline.iter().enumerate() { diff --git a/src/app/mod.rs b/src/app/mod.rs index 1e0262a..d989b14 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -4,24 +4,24 @@ use iced::{ }; use smol::Timer; -use crate::app::cpu_graph::CpuGraph; +use crate::{app::cpu_graph::CpuGraph, sysinfo_snapshot::SysinfoSnapshot}; mod cpu_graph; enum Message { - Cpu(f32), + Info(SysinfoSnapshot), } +#[derive(Default)] struct State { - cpu_graph: CpuGraph, + total_cpu_graph: CpuGraph, + cpu_graphs: Vec<(String, CpuGraph)>, } impl State { fn new() -> (Self, Task) { ( - Self { - cpu_graph: Default::default(), - }, + Self::default(), Task::run( channel(1, async |mut sender| { let mut sys = sysinfo::System::new(); @@ -34,28 +34,58 @@ impl State { loop { sys.refresh_specifics(refreshes); Timer::after(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL).await; - sender.send(sys.global_cpu_usage()).await.unwrap(); + sender.send(SysinfoSnapshot::from(&sys)).await.unwrap(); } }), - Message::Cpu, + Message::Info, ), ) } fn update(&mut self, message: Message) -> Task { match message { - Message::Cpu(value) => { - self.cpu_graph.update(value); - self.cpu_graph.cache.clear(); + Message::Info(snapshot) => { + self.total_cpu_graph.update(snapshot.cpu_total); + self.total_cpu_graph.cache.clear(); + + if self.cpu_graphs.len() != snapshot.cpus.len() { + self.cpu_graphs = snapshot + .cpus + .into_iter() + .map(|(name, usage)| (name, CpuGraph::new(usage))) + .collect(); + } else { + for ((_, graph), (_, usage)) in + self.cpu_graphs.iter_mut().zip(snapshot.cpus.into_iter()) + { + graph.update(usage); + graph.cache.clear(); + } + } } } Task::none() } fn view(&self) -> Element<'_, Message, Theme, Renderer> { - widget::Canvas::new(&self.cpu_graph) - .width(Length::Fill) - .height(Length::Fill) - .into() + widget::row([ + widget::Canvas::new(&self.total_cpu_graph) + .width(Length::Fill) + .height(Length::Fill) + .into(), + widget::grid(self.cpu_graphs.iter().map(|(name, graph)| { + widget::row([ + // widget::text(name).into(), + widget::Canvas::new(graph) + .width(Length::Fill) + .height(Length::Fill) + .into(), + ]) + .into() + })) + // .columns(8) + .into(), + ]) + .into() } } diff --git a/src/main.rs b/src/main.rs index 2e13470..fa3e73b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ mod app; +mod sysinfo_snapshot; fn main() -> Result<(), impl std::error::Error> { app::run() diff --git a/src/sysinfo_snapshot.rs b/src/sysinfo_snapshot.rs new file mode 100644 index 0000000..2e6955c --- /dev/null +++ b/src/sysinfo_snapshot.rs @@ -0,0 +1,18 @@ +#[derive(Clone, Debug)] +pub struct SysinfoSnapshot { + pub cpu_total: f32, + pub cpus: Vec<(String, f32)>, +} + +impl From<&sysinfo::System> for SysinfoSnapshot { + fn from(value: &sysinfo::System) -> Self { + Self { + cpu_total: value.global_cpu_usage(), + cpus: value + .cpus() + .iter() + .map(|cpu| (cpu.name().into(), cpu.cpu_usage())) + .collect(), + } + } +}