From d068d110e267be0b782139501c7fca22f8eb8da9 Mon Sep 17 00:00:00 2001 From: electria Date: Sun, 16 Aug 2026 08:41:53 -0700 Subject: [PATCH 1/6] feat: memory and swap graphs the values are in the SysinfoSnapshot verbatim, in case they might be displayed in another way (textually) --- src/app/mod.rs | 45 ++++++++++++++++++++++++++++++++--------- src/sysinfo_snapshot.rs | 10 +++++++++ 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index c4a0eb6..c0d5ab8 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -16,6 +16,8 @@ enum Message { struct State { total_cpu_graph: Graph, cpu_graphs: Vec, + memory_graph: Graph, + swap_graph: Graph, } impl State { @@ -26,6 +28,7 @@ impl State { stream::channel(1, async |mut sender| { let mut sys = sysinfo::System::new(); let refreshes = sysinfo::RefreshKind::nothing() + .with_memory(sysinfo::MemoryRefreshKind::everything()) .with_cpu(sysinfo::CpuRefreshKind::nothing().with_cpu_usage()); sys.refresh_specifics(refreshes); @@ -47,6 +50,14 @@ impl State { self.total_cpu_graph.update(snapshot.cpu_total); self.total_cpu_graph.cache.clear(); + self.memory_graph + .update(snapshot.memory as f32 / snapshot.memory_total as f32 * 100.); + self.memory_graph.cache.clear(); + + self.swap_graph + .update(snapshot.swap as f32 / snapshot.swap_total as f32 * 100.); + self.swap_graph.cache.clear(); + if self.cpu_graphs.len() != snapshot.cpus.len() { self.cpu_graphs = snapshot .cpus @@ -66,18 +77,32 @@ impl State { Task::none() } fn view(&self) -> Element<'_, Message, Theme, Renderer> { - widget::row([ - widget::Canvas::new(&self.total_cpu_graph) - .width(Length::Fill) + widget::column([ + widget::row([ + widget::Canvas::new(&self.total_cpu_graph) + .width(Length::Fill) + .height(Length::Fill) + .into(), + widget::grid( + self.cpu_graphs + .iter() + .map(|graph| widget::Canvas::new(graph).into()), + ) + .columns(2) .height(Length::Fill) .into(), - widget::grid( - self.cpu_graphs - .iter() - .map(|graph| widget::Canvas::new(graph).into()), - ) - .columns(2) - .height(Length::Fill) + ]) + .into(), + widget::row([ + widget::canvas(&self.memory_graph) + .width(Length::Fill) + .height(Length::Fill) + .into(), + widget::canvas(&self.swap_graph) + .width(Length::Fill) + .height(Length::Fill) + .into(), + ]) .into(), ]) .into() diff --git a/src/sysinfo_snapshot.rs b/src/sysinfo_snapshot.rs index cb55f06..340a215 100644 --- a/src/sysinfo_snapshot.rs +++ b/src/sysinfo_snapshot.rs @@ -2,6 +2,11 @@ pub struct SysinfoSnapshot { pub cpu_total: f32, pub cpus: Vec, + + pub memory: u64, + pub memory_total: u64, + pub swap: u64, + pub swap_total: u64, } impl From<&sysinfo::System> for SysinfoSnapshot { @@ -9,6 +14,11 @@ impl From<&sysinfo::System> for SysinfoSnapshot { Self { cpu_total: value.global_cpu_usage(), cpus: value.cpus().iter().map(|cpu| cpu.cpu_usage()).collect(), + + memory: value.used_memory(), + memory_total: value.total_memory(), + swap: value.used_swap(), + swap_total: value.total_swap(), } } } From 8589c0c66637e2bda866a6a67e85fd784c65e246 Mon Sep 17 00:00:00 2001 From: electria Date: Sun, 16 Aug 2026 10:16:52 -0700 Subject: [PATCH 2/6] refactor: consistency in widget creation --- src/app/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index c0d5ab8..600c93f 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -79,7 +79,7 @@ impl State { fn view(&self) -> Element<'_, Message, Theme, Renderer> { widget::column([ widget::row([ - widget::Canvas::new(&self.total_cpu_graph) + widget::canvas(&self.total_cpu_graph) .width(Length::Fill) .height(Length::Fill) .into(), From 865fecd694ba06dfb7074211cbdb2c57a91f2657 Mon Sep 17 00:00:00 2001 From: electria Date: Sun, 16 Aug 2026 10:18:24 -0700 Subject: [PATCH 3/6] refactor: clear cache in Graph::update --- src/app/graph.rs | 2 ++ src/app/mod.rs | 4 ---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/app/graph.rs b/src/app/graph.rs index cdbd212..5b93a80 100644 --- a/src/app/graph.rs +++ b/src/app/graph.rs @@ -30,6 +30,8 @@ impl Graph { if self.timeline.len() > 500 { self.timeline.pop_front(); } + + self.cache.clear(); } } diff --git a/src/app/mod.rs b/src/app/mod.rs index 600c93f..68197fa 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -48,15 +48,12 @@ impl State { match message { Message::Info(snapshot) => { self.total_cpu_graph.update(snapshot.cpu_total); - self.total_cpu_graph.cache.clear(); self.memory_graph .update(snapshot.memory as f32 / snapshot.memory_total as f32 * 100.); - self.memory_graph.cache.clear(); self.swap_graph .update(snapshot.swap as f32 / snapshot.swap_total as f32 * 100.); - self.swap_graph.cache.clear(); if self.cpu_graphs.len() != snapshot.cpus.len() { self.cpu_graphs = snapshot @@ -68,7 +65,6 @@ impl State { for (graph, value) in self.cpu_graphs.iter_mut().zip(snapshot.cpus.into_iter()) { graph.update(value); - graph.cache.clear(); } } } From 79aff5185675a331ba48e32c566c7968d67d8ed4 Mon Sep 17 00:00:00 2001 From: electria Date: Sun, 16 Aug 2026 10:28:38 -0700 Subject: [PATCH 4/6] fix: send before sleeping should make the initial graph come in ~200ms faster, for free --- src/app/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index 68197fa..260003e 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -36,8 +36,8 @@ impl State { loop { sys.refresh_specifics(refreshes); - Timer::after(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL).await; sender.send(SysinfoSnapshot::from(&sys)).await.unwrap(); + Timer::after(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL).await; } }), Message::Info, From 534b6cde9916c5827f74ea3b53929017cb6e967f Mon Sep 17 00:00:00 2001 From: electria Date: Sun, 16 Aug 2026 10:49:41 -0700 Subject: [PATCH 5/6] feat: add uptime to title it will be displayed somewhere else later, hopefully with better formatting too. --- src/app/mod.rs | 11 +++++++++++ src/sysinfo_snapshot.rs | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/app/mod.rs b/src/app/mod.rs index 260003e..37d6376 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1,3 +1,5 @@ +use std::time::Duration; + use iced::{ Color, Element, Length, Renderer, Task, Theme, application, futures::SinkExt, stream, theme, widget, @@ -14,6 +16,8 @@ enum Message { #[derive(Default)] struct State { + uptime: Duration, + total_cpu_graph: Graph, cpu_graphs: Vec, memory_graph: Graph, @@ -47,6 +51,8 @@ impl State { fn update(&mut self, message: Message) -> Task { match message { Message::Info(snapshot) => { + self.uptime = snapshot.uptime(); + self.total_cpu_graph.update(snapshot.cpu_total); self.memory_graph @@ -103,10 +109,15 @@ impl State { ]) .into() } + + fn title(&self) -> String { + format!("itop {:?}", self.uptime) + } } pub fn run() -> Result<(), impl std::error::Error> { application(State::new, State::update, State::view) + .title(State::title) .theme(theme::Theme::custom( "high-contrast-dark", theme::Palette { diff --git a/src/sysinfo_snapshot.rs b/src/sysinfo_snapshot.rs index 340a215..c4bee85 100644 --- a/src/sysinfo_snapshot.rs +++ b/src/sysinfo_snapshot.rs @@ -1,5 +1,9 @@ +use std::time::Duration; + #[derive(Clone, Debug)] pub struct SysinfoSnapshot { + uptime: u64, + pub cpu_total: f32, pub cpus: Vec, @@ -8,10 +12,17 @@ pub struct SysinfoSnapshot { pub swap: u64, pub swap_total: u64, } +impl SysinfoSnapshot { + pub fn uptime(&self) -> Duration { + Duration::from_secs(self.uptime) + } +} impl From<&sysinfo::System> for SysinfoSnapshot { fn from(value: &sysinfo::System) -> Self { Self { + uptime: sysinfo::System::uptime(), + cpu_total: value.global_cpu_usage(), cpus: value.cpus().iter().map(|cpu| cpu.cpu_usage()).collect(), From 171194e99f0168f6bbf6b2fe058763e2a3bd6584 Mon Sep 17 00:00:00 2001 From: electria Date: Sun, 16 Aug 2026 11:02:32 -0700 Subject: [PATCH 6/6] refactor: split info_stream into module in preparation to create a client-server model --- src/app/info_stream.rs | 28 ++++++++++++++++++++++++++++ src/app/mod.rs | 26 +++----------------------- 2 files changed, 31 insertions(+), 23 deletions(-) create mode 100644 src/app/info_stream.rs diff --git a/src/app/info_stream.rs b/src/app/info_stream.rs new file mode 100644 index 0000000..e7d7632 --- /dev/null +++ b/src/app/info_stream.rs @@ -0,0 +1,28 @@ +use iced::{ + futures::{self, SinkExt}, + stream, +}; +use smol::Timer; + +use crate::sysinfo_snapshot::SysinfoSnapshot; + +pub fn create() -> impl futures::Stream { + stream::channel( + 1, + async |mut sender: futures::channel::mpsc::Sender| { + let mut sys = sysinfo::System::new(); + let refreshes = sysinfo::RefreshKind::nothing() + .with_memory(sysinfo::MemoryRefreshKind::everything()) + .with_cpu(sysinfo::CpuRefreshKind::nothing().with_cpu_usage()); + + sys.refresh_specifics(refreshes); + Timer::after(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL).await; + + loop { + sys.refresh_specifics(refreshes); + sender.send(SysinfoSnapshot::from(&sys)).await.unwrap(); + Timer::after(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL).await; + } + }, + ) +} diff --git a/src/app/mod.rs b/src/app/mod.rs index 37d6376..427e90e 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1,14 +1,11 @@ use std::time::Duration; -use iced::{ - Color, Element, Length, Renderer, Task, Theme, application, futures::SinkExt, stream, theme, - widget, -}; -use smol::Timer; +use iced::{Color, Element, Length, Renderer, Task, Theme, application, theme, widget}; use crate::{app::graph::Graph, sysinfo_snapshot::SysinfoSnapshot}; mod graph; +mod info_stream; enum Message { Info(SysinfoSnapshot), @@ -28,24 +25,7 @@ impl State { fn new() -> (Self, Task) { ( Self::default(), - Task::run( - stream::channel(1, async |mut sender| { - let mut sys = sysinfo::System::new(); - let refreshes = sysinfo::RefreshKind::nothing() - .with_memory(sysinfo::MemoryRefreshKind::everything()) - .with_cpu(sysinfo::CpuRefreshKind::nothing().with_cpu_usage()); - - sys.refresh_specifics(refreshes); - Timer::after(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL).await; - - loop { - sys.refresh_specifics(refreshes); - sender.send(SysinfoSnapshot::from(&sys)).await.unwrap(); - Timer::after(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL).await; - } - }), - Message::Info, - ), + Task::run(info_stream::create(), Message::Info), ) } fn update(&mut self, message: Message) -> Task {