Compare commits

...
Author SHA1 Message Date
171194e99f
refactor: split info_stream into module
in preparation to create a client-server model
2026-08-16 11:02:32 -07:00
534b6cde99
feat: add uptime to title
it will be displayed somewhere else later,
hopefully with better formatting too.
2026-08-16 10:49:41 -07:00
79aff51856
fix: send before sleeping
should make the initial graph come in ~200ms faster, for free
2026-08-16 10:29:25 -07:00
865fecd694
refactor: clear cache in Graph::update 2026-08-16 10:18:24 -07:00
8589c0c666
refactor: consistency in widget creation 2026-08-16 10:16:52 -07:00
d068d110e2
feat: memory and swap graphs
the values are in the SysinfoSnapshot verbatim,
in case they might be displayed in another way (textually)
2026-08-16 08:41:53 -07:00
4 changed files with 97 additions and 34 deletions

View file

@ -30,6 +30,8 @@ impl Graph {
if self.timeline.len() > 500 { if self.timeline.len() > 500 {
self.timeline.pop_front(); self.timeline.pop_front();
} }
self.cache.clear();
} }
} }

28
src/app/info_stream.rs Normal file
View file

@ -0,0 +1,28 @@
use iced::{
futures::{self, SinkExt},
stream,
};
use smol::Timer;
use crate::sysinfo_snapshot::SysinfoSnapshot;
pub fn create() -> impl futures::Stream<Item = SysinfoSnapshot> {
stream::channel(
1,
async |mut sender: futures::channel::mpsc::Sender<SysinfoSnapshot>| {
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;
}
},
)
}

View file

@ -1,12 +1,11 @@
use iced::{ use std::time::Duration;
Color, Element, Length, Renderer, Task, Theme, application, futures::SinkExt, stream, theme,
widget, use iced::{Color, Element, Length, Renderer, Task, Theme, application, theme, widget};
};
use smol::Timer;
use crate::{app::graph::Graph, sysinfo_snapshot::SysinfoSnapshot}; use crate::{app::graph::Graph, sysinfo_snapshot::SysinfoSnapshot};
mod graph; mod graph;
mod info_stream;
enum Message { enum Message {
Info(SysinfoSnapshot), Info(SysinfoSnapshot),
@ -14,38 +13,33 @@ enum Message {
#[derive(Default)] #[derive(Default)]
struct State { struct State {
uptime: Duration,
total_cpu_graph: Graph, total_cpu_graph: Graph,
cpu_graphs: Vec<Graph>, cpu_graphs: Vec<Graph>,
memory_graph: Graph,
swap_graph: Graph,
} }
impl State { impl State {
fn new() -> (Self, Task<Message>) { fn new() -> (Self, Task<Message>) {
( (
Self::default(), Self::default(),
Task::run( Task::run(info_stream::create(), Message::Info),
stream::channel(1, async |mut sender| {
let mut sys = sysinfo::System::new();
let refreshes = sysinfo::RefreshKind::nothing()
.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);
Timer::after(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL).await;
sender.send(SysinfoSnapshot::from(&sys)).await.unwrap();
}
}),
Message::Info,
),
) )
} }
fn update(&mut self, message: Message) -> Task<Message> { fn update(&mut self, message: Message) -> Task<Message> {
match message { match message {
Message::Info(snapshot) => { Message::Info(snapshot) => {
self.uptime = snapshot.uptime();
self.total_cpu_graph.update(snapshot.cpu_total); 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.swap_graph
.update(snapshot.swap as f32 / snapshot.swap_total as f32 * 100.);
if self.cpu_graphs.len() != snapshot.cpus.len() { if self.cpu_graphs.len() != snapshot.cpus.len() {
self.cpu_graphs = snapshot self.cpu_graphs = snapshot
@ -57,7 +51,6 @@ impl State {
for (graph, value) in self.cpu_graphs.iter_mut().zip(snapshot.cpus.into_iter()) for (graph, value) in self.cpu_graphs.iter_mut().zip(snapshot.cpus.into_iter())
{ {
graph.update(value); graph.update(value);
graph.cache.clear();
} }
} }
} }
@ -66,26 +59,45 @@ impl State {
Task::none() Task::none()
} }
fn view(&self) -> Element<'_, Message, Theme, Renderer> { fn view(&self) -> Element<'_, Message, Theme, Renderer> {
widget::row([ widget::column([
widget::Canvas::new(&self.total_cpu_graph) widget::row([
.width(Length::Fill) widget::canvas(&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) .height(Length::Fill)
.into(), .into(),
widget::grid( ])
self.cpu_graphs .into(),
.iter() widget::row([
.map(|graph| widget::Canvas::new(graph).into()), widget::canvas(&self.memory_graph)
) .width(Length::Fill)
.columns(2) .height(Length::Fill)
.height(Length::Fill) .into(),
widget::canvas(&self.swap_graph)
.width(Length::Fill)
.height(Length::Fill)
.into(),
])
.into(), .into(),
]) ])
.into() .into()
} }
fn title(&self) -> String {
format!("itop {:?}", self.uptime)
}
} }
pub fn run() -> Result<(), impl std::error::Error> { pub fn run() -> Result<(), impl std::error::Error> {
application(State::new, State::update, State::view) application(State::new, State::update, State::view)
.title(State::title)
.theme(theme::Theme::custom( .theme(theme::Theme::custom(
"high-contrast-dark", "high-contrast-dark",
theme::Palette { theme::Palette {

View file

@ -1,14 +1,35 @@
use std::time::Duration;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SysinfoSnapshot { pub struct SysinfoSnapshot {
uptime: u64,
pub cpu_total: f32, pub cpu_total: f32,
pub cpus: Vec<f32>, pub cpus: Vec<f32>,
pub memory: u64,
pub memory_total: u64,
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 { impl From<&sysinfo::System> for SysinfoSnapshot {
fn from(value: &sysinfo::System) -> Self { fn from(value: &sysinfo::System) -> Self {
Self { Self {
uptime: sysinfo::System::uptime(),
cpu_total: value.global_cpu_usage(), cpu_total: value.global_cpu_usage(),
cpus: value.cpus().iter().map(|cpu| cpu.cpu_usage()).collect(), 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(),
} }
} }
} }