this is unnacceptably unperformant; I think I've reached the point at which sysinfo doesn't do what I want, which is keep track of a table of processes efficiently enough.
205 lines
6.3 KiB
Rust
205 lines
6.3 KiB
Rust
use std::time::Duration;
|
|
|
|
use iced::{
|
|
Color, Error, Event, Length, Renderer, Subscription, Task, Theme, application, event,
|
|
keyboard::{self, Key},
|
|
theme, widget,
|
|
};
|
|
|
|
use crate::{
|
|
app::{graph::Graph, utils::maybe_widget},
|
|
sysinfo_snapshot::{ProcessSnapshot, SysinfoSnapshot},
|
|
utils::Toggle,
|
|
};
|
|
|
|
mod graph;
|
|
mod info_stream;
|
|
mod utils;
|
|
|
|
type Element<'a> = iced::Element<'a, Message, Theme, Renderer>;
|
|
|
|
enum Message {
|
|
ToggleMain,
|
|
ToggleCpu,
|
|
ToggleMemory,
|
|
ToggleProcesses,
|
|
|
|
Info(SysinfoSnapshot),
|
|
}
|
|
|
|
#[derive(Default)]
|
|
struct State {
|
|
uptime: Duration,
|
|
|
|
is_main_section_shown: bool,
|
|
total_cpu_graph: Graph,
|
|
|
|
is_cpu_section_shown: bool,
|
|
cpu_graphs: Vec<Graph>,
|
|
|
|
is_memory_section_shown: bool,
|
|
memory_graph: Graph,
|
|
swap_graph: Graph,
|
|
|
|
is_processes_section_shown: bool,
|
|
processes: Vec<ProcessSnapshot>,
|
|
}
|
|
|
|
impl State {
|
|
fn new() -> (Self, Task<Message>) {
|
|
let mut state = Self {
|
|
is_main_section_shown: true,
|
|
..Default::default()
|
|
};
|
|
state.total_cpu_graph.show_borders = false;
|
|
|
|
(state, info_stream::create())
|
|
}
|
|
fn update(&mut self, message: Message) -> Task<Message> {
|
|
match message {
|
|
Message::ToggleMain => {
|
|
self.is_main_section_shown.toggle();
|
|
self.check_borders();
|
|
}
|
|
Message::ToggleCpu => {
|
|
self.is_cpu_section_shown.toggle();
|
|
self.check_borders();
|
|
}
|
|
Message::ToggleMemory => {
|
|
self.is_memory_section_shown.toggle();
|
|
self.check_borders();
|
|
}
|
|
Message::ToggleProcesses => {
|
|
self.is_processes_section_shown.toggle();
|
|
self.check_borders();
|
|
}
|
|
|
|
Message::Info(snapshot) => {
|
|
self.uptime = snapshot.uptime();
|
|
|
|
self.total_cpu_graph.update(snapshot.cpu_total);
|
|
|
|
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() {
|
|
self.cpu_graphs = snapshot
|
|
.cpus
|
|
.into_iter()
|
|
.map(|value| Graph::new(value))
|
|
.collect();
|
|
} else {
|
|
self.cpu_graphs
|
|
.iter_mut()
|
|
.zip(snapshot.cpus.into_iter())
|
|
.for_each(|(graph, value)| graph.update(value));
|
|
}
|
|
|
|
self.processes = snapshot.processes;
|
|
self.processes
|
|
.sort_by(|a, b| b.cpu_usage.total_cmp(&a.cpu_usage));
|
|
}
|
|
}
|
|
|
|
Task::none()
|
|
}
|
|
fn view(&self) -> Element<'_> {
|
|
widget::column([
|
|
widget::row([
|
|
maybe_widget(self.is_main_section_shown, || {
|
|
widget::canvas(&self.total_cpu_graph)
|
|
.width(Length::Fill)
|
|
.height(Length::Fill)
|
|
.into()
|
|
}),
|
|
maybe_widget(self.is_cpu_section_shown, || {
|
|
widget::grid(
|
|
self.cpu_graphs
|
|
.iter()
|
|
.map(|graph| widget::Canvas::new(graph).into()),
|
|
)
|
|
.columns(2)
|
|
.height(Length::Fill)
|
|
.into()
|
|
}),
|
|
])
|
|
.into(),
|
|
widget::row([
|
|
maybe_widget(self.is_memory_section_shown, || {
|
|
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()
|
|
}),
|
|
maybe_widget(self.is_processes_section_shown, || {
|
|
widget::scrollable(
|
|
widget::grid(self.processes.iter().flat_map(|process| {
|
|
[
|
|
widget::text(&process.name).into(),
|
|
widget::text(process.cpu_usage).into(),
|
|
widget::text(format!("{:?}", process.cpu_time())).into(),
|
|
widget::text(process.pid).into(),
|
|
]
|
|
}))
|
|
.height(Length::Shrink)
|
|
.columns(4),
|
|
)
|
|
.width(Length::Fill)
|
|
.height(Length::Fill)
|
|
.into()
|
|
}),
|
|
])
|
|
.into(),
|
|
])
|
|
.into()
|
|
}
|
|
|
|
fn subscription(&self) -> Subscription<Message> {
|
|
event::listen().filter_map(|event| match event {
|
|
Event::Keyboard(keyboard::Event::KeyPressed { modified_key, .. }) => {
|
|
match modified_key.as_ref() {
|
|
Key::Character("M" | "1") => Some(Message::ToggleMain),
|
|
Key::Character("c" | "2") => Some(Message::ToggleCpu),
|
|
Key::Character("m" | "3") => Some(Message::ToggleMemory),
|
|
Key::Character("p" | "4") => Some(Message::ToggleProcesses),
|
|
_ => None,
|
|
}
|
|
}
|
|
_ => None,
|
|
})
|
|
}
|
|
|
|
fn title(&self) -> String {
|
|
format!("itop {:?}", self.uptime)
|
|
}
|
|
|
|
fn check_borders(&mut self) {
|
|
self.total_cpu_graph.show_borders =
|
|
self.is_cpu_section_shown || self.is_memory_section_shown;
|
|
}
|
|
}
|
|
|
|
pub fn run() -> Result<(), Error> {
|
|
application(State::new, State::update, State::view)
|
|
.subscription(State::subscription)
|
|
.title(State::title)
|
|
.theme(theme::Theme::custom(
|
|
"high-contrast-dark",
|
|
theme::Palette {
|
|
background: Color::BLACK,
|
|
text: Color::WHITE,
|
|
..theme::Palette::DARK
|
|
},
|
|
))
|
|
.run()
|
|
}
|