use gtk::glib;
use gtk::prelude::*;
use sysinfo::{self, ComponentExt, CpuExt, SystemExt};
use std::cell::RefCell;
use std::iter;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use crate::graph::GraphWidget;
use crate::settings::Settings;
use crate::utils::{format_number, graph_label_units, RotateVec};
pub fn create_header(
label_text: &str,
parent_layout: >k::Box,
display_graph: bool,
) -> gtk::CheckButton {
let check_box = gtk::CheckButton::builder()
.label("Graph view")
.active(display_graph)
.halign(gtk::Align::End)
.build();
let label = gtk::Label::new(Some(label_text));
let grid = gtk::Grid::builder()
.hexpand(true)
.column_homogeneous(true)
.build();
grid.attach(>k::Label::new(None), 0, 0, 2, 1); grid.attach(&label, 1, 0, 2, 1);
grid.attach(&check_box, 3, 0, 1, 1);
parent_layout.append(&grid);
check_box
}
pub fn create_progress_bar(
non_graph_layout: >k::Grid,
line: i32,
label: &str,
text: &str,
) -> gtk::ProgressBar {
let p = gtk::ProgressBar::builder()
.text(text)
.show_text(true)
.build();
let l = gtk::Label::new(Some(label));
non_graph_layout.attach(&l, 0, line, 1, 1);
non_graph_layout.attach(&p, 1, line, 11, 1);
p
}
#[allow(dead_code)]
pub struct DisplaySysInfo {
procs: Rc<RefCell<Vec<gtk::ProgressBar>>>,
ram: gtk::ProgressBar,
swap: gtk::ProgressBar,
vertical_layout: gtk::Box,
components: Vec<gtk::Label>,
cpu_usage_history: Rc<RefCell<GraphWidget>>,
ram_usage_history: Rc<RefCell<GraphWidget>>,
temperature_usage_history: Rc<RefCell<GraphWidget>>,
pub ram_check_box: gtk::CheckButton,
pub swap_check_box: gtk::CheckButton,
pub temperature_check_box: Option<gtk::CheckButton>,
}
impl DisplaySysInfo {
pub fn new(
sys: &Arc<Mutex<sysinfo::System>>,
stack: >k::Stack,
settings: &Settings,
) -> DisplaySysInfo {
let vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0);
let mut procs = Vec::new();
let scroll = gtk::ScrolledWindow::new();
let mut components = vec![];
let cpu_usage_history = GraphWidget::new(None, false);
cpu_usage_history.set_margin_start(3);
cpu_usage_history.set_margin_end(6);
cpu_usage_history.set_labels_callback(Some(Box::new(|_| {
[
"100".to_string(),
"50".to_string(),
"0".to_string(),
"%".to_string(),
]
})));
let sys = sys.lock().expect("failed to lock in DisplaySysInfo::new");
let ram_usage_history = GraphWidget::new(Some(sys.total_memory() as f32), true);
ram_usage_history.set_margin_start(3);
ram_usage_history.set_margin_end(6);
ram_usage_history.set_labels_callback(Some(Box::new(graph_label_units)));
let temperature_usage_history = GraphWidget::new(Some(1.), false);
temperature_usage_history.set_margin_start(3);
temperature_usage_history.set_margin_end(6);
temperature_usage_history.set_overhead(Some(20.));
temperature_usage_history.set_labels_callback(Some(Box::new(|v| {
[
format!("{:.1}", v),
format!("{:.1}", v / 2.),
"0".to_string(),
"°C".to_string(),
]
})));
let mut check_box3 = None;
vertical_layout.set_spacing(5);
vertical_layout.set_margin_top(10);
vertical_layout.set_margin_bottom(10);
let non_graph_layout = gtk::Grid::builder()
.column_homogeneous(true)
.margin_end(5)
.build();
let non_graph_layout2 = gtk::Grid::builder()
.column_homogeneous(true)
.margin_start(5)
.build();
let non_graph_layout3 = gtk::Box::new(gtk::Orientation::Vertical, 0);
let label = gtk::Label::new(Some("Total CPU usage"));
label.set_margin_start(7);
vertical_layout.append(&label);
procs.push(gtk::ProgressBar::new());
{
procs.push(gtk::ProgressBar::new());
let p: >k::ProgressBar = &procs[0];
p.set_margin_end(5);
p.set_margin_start(5);
p.set_show_text(true);
let processor = sys.global_cpu_info();
p.set_text(Some(&format!("{:.1} %", processor.cpu_usage())));
p.set_fraction(f64::from(processor.cpu_usage() / 100.));
vertical_layout.append(p);
}
let check_box = create_header("Processors usage", &vertical_layout, settings.display_graph);
for (i, pro) in sys.cpus().iter().enumerate() {
procs.push(gtk::ProgressBar::new());
let p: >k::ProgressBar = &procs[i + 1];
let l = gtk::Label::new(Some(&format!("{}", i)));
p.set_text(Some(&format!("{:.1} %", pro.cpu_usage())));
p.set_show_text(true);
p.set_fraction(f64::from(pro.cpu_usage()));
non_graph_layout.attach(&l, 0, i as i32 - 1, 1, 1);
non_graph_layout.attach(p, 1, i as i32 - 1, 11, 1);
cpu_usage_history.push(
RotateVec::new(iter::repeat(0f32).take(61).collect()),
&format!("processor {}", i),
None,
);
}
vertical_layout.append(&non_graph_layout);
vertical_layout.append(&cpu_usage_history);
let check_box2 = create_header("Memory usage", &vertical_layout, settings.display_graph);
let ram = create_progress_bar(&non_graph_layout2, 0, "RAM", "");
let swap = create_progress_bar(&non_graph_layout2, 1, "Swap", "");
non_graph_layout2.set_margin_start(15);
vertical_layout.append(&non_graph_layout2);
ram_usage_history.push(
RotateVec::new(iter::repeat(0f32).take(61).collect()),
"RAM",
Some(4),
);
ram_usage_history.push(
RotateVec::new(iter::repeat(0f32).take(61).collect()),
"Swap",
Some(2),
);
vertical_layout.append(&ram_usage_history);
if !sys.components().is_empty() {
check_box3 = Some(create_header(
"Components' temperature",
&vertical_layout,
settings.display_graph,
));
for component in sys.components() {
let horizontal_layout = gtk::Box::new(gtk::Orientation::Horizontal, 10);
let temp = gtk::Label::new(Some(&format!("{:.1} °C", component.temperature())));
horizontal_layout.append(>k::Label::new(Some(component.label())));
horizontal_layout.append(&temp);
horizontal_layout.set_homogeneous(true);
non_graph_layout3.append(&horizontal_layout);
components.push(temp);
temperature_usage_history.push(
RotateVec::new(iter::repeat(0f32).take(61).collect()),
component.label(),
None,
);
}
vertical_layout.append(&non_graph_layout3);
vertical_layout.append(&temperature_usage_history);
}
let cpu_usage_history = Rc::new(RefCell::new(cpu_usage_history));
let ram_usage_history = Rc::new(RefCell::new(ram_usage_history));
let temperature_usage_history = Rc::new(RefCell::new(temperature_usage_history));
scroll.set_child(Some(&vertical_layout));
stack.add_titled(&scroll, Some("System"), "System");
cpu_usage_history.borrow().hide();
ram_usage_history.borrow().hide();
temperature_usage_history.borrow().hide();
check_box.connect_toggled(
glib::clone!(@weak non_graph_layout, @weak cpu_usage_history => move |c| {
show_if_necessary(c, &cpu_usage_history.borrow(), &non_graph_layout);
}),
);
show_if_necessary(&check_box, &cpu_usage_history.borrow(), &non_graph_layout);
check_box2.connect_toggled(
glib::clone!(@weak non_graph_layout2, @weak ram_usage_history => move |c| {
show_if_necessary(c, &ram_usage_history.borrow(), &non_graph_layout2);
}),
);
show_if_necessary(&check_box2, &ram_usage_history.borrow(), &non_graph_layout2);
if let Some(ref check_box3) = check_box3 {
check_box3.connect_toggled(
glib::clone!(@weak non_graph_layout3, @weak temperature_usage_history => move |c| {
show_if_necessary(c, &temperature_usage_history.borrow(), &non_graph_layout3);
}),
);
show_if_necessary(
check_box3,
&temperature_usage_history.borrow(),
&non_graph_layout3,
);
}
let mut tmp = DisplaySysInfo {
procs: Rc::new(RefCell::new(procs)),
ram,
swap,
vertical_layout,
components,
cpu_usage_history,
ram_usage_history,
ram_check_box: check_box,
swap_check_box: check_box2,
temperature_usage_history,
temperature_check_box: check_box3,
};
tmp.update_system_info(&sys, settings.display_fahrenheit);
tmp
}
pub fn set_checkboxes_state(&self, active: bool) {
self.ram_check_box.set_active(active);
self.swap_check_box.set_active(active);
if let Some(ref temperature_check_box) = self.temperature_check_box {
temperature_check_box.set_active(active);
}
}
pub fn update_system_info(&mut self, sys: &sysinfo::System, display_fahrenheit: bool) {
let disp = |total, used| {
format!(
"{} / {}",
format_number(used),
format_number(total) )
};
let total_ram = sys.total_memory();
let used = sys.used_memory();
self.ram.set_text(Some(&disp(total_ram, used)));
if total_ram != 0 {
self.ram.set_fraction(used as f64 / total_ram as f64);
} else {
self.ram.set_fraction(0.0);
}
{
let r = self.ram_usage_history.borrow_mut();
r.data(0, |d| {
d.move_start();
if let Some(p) = d.get_mut(0) {
*p = used as _;
}
});
}
let total = ::std::cmp::max(sys.total_swap(), total_ram);
let used = sys.used_swap();
self.swap.set_text(Some(&disp(sys.total_swap(), used)));
let mut fraction = if total != 0 {
used as f64 / total as f64
} else {
0f64
};
if fraction.is_nan() {
fraction = 0.;
}
self.swap.set_fraction(fraction);
{
let r = self.ram_usage_history.borrow_mut();
r.data(1, |d| {
d.move_start();
if let Some(p) = d.get_mut(0) {
*p = used as _;
}
});
}
let t = self.temperature_usage_history.borrow_mut();
for (pos, (component, label)) in sys
.components()
.iter()
.zip(self.components.iter())
.enumerate()
{
t.data(pos, |d| {
d.move_start();
if let Some(t) = d.get_mut(0) {
*t = component.temperature();
}
if let Some(t) = d.get_mut(0) {
*t = component.temperature();
}
});
if display_fahrenheit {
label.set_text(&format!("{:.1} °F", component.temperature() * 1.8 + 32.));
} else {
label.set_text(&format!("{:.1} °C", component.temperature()));
}
}
}
pub fn update_system_info_display(&mut self, sys: &sysinfo::System) {
let v = &*self.procs.borrow_mut();
let h = &mut *self.cpu_usage_history.borrow_mut();
v[0].set_text(Some(&format!("{:.1} %", sys.global_cpu_info().cpu_usage())));
v[0].set_show_text(true);
v[0].set_fraction(f64::from(sys.global_cpu_info().cpu_usage() / 100.));
for (i, pro) in sys.cpus().iter().enumerate() {
let i = i + 1;
v[i].set_text(Some(&format!("{:.1} %", pro.cpu_usage())));
v[i].set_show_text(true);
v[i].set_fraction(f64::from(pro.cpu_usage() / 100.));
h.data(i - 1, |d| {
d.move_start();
if let Some(h) = d.get_mut(0) {
*h = pro.cpu_usage() / 100.;
}
});
}
h.queue_draw();
self.ram_usage_history.borrow().queue_draw();
self.temperature_usage_history.borrow().queue_draw();
}
}
pub fn show_if_necessary<U: gtk::glib::IsA<gtk::CheckButton>, T: WidgetExt>(
check_box: &U,
proc_horizontal_layout: &GraphWidget,
non_graph_layout: &T,
) {
if check_box.is_active() {
proc_horizontal_layout.show();
non_graph_layout.hide();
} else {
non_graph_layout.show();
proc_horizontal_layout.hide();
}
}