use super::{MyView, SortOrder};
use crate::gui::styles;
use crate::gui::styles::colors::*;
use fltk::{app::MouseButton, enums::*, prelude::*, *};
use parking_lot::Mutex;
use std::str::FromStr;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use sysinfo::{Pid, ProcessesToUpdate, Signal, System, Users};
struct ProcToggle {
b: button::RadioButton,
}
impl ProcToggle {
pub fn new(label: &str, ord: Arc<Mutex<SortOrder>>) -> Self {
let mut b = button::RadioButton::default()
.with_size(70, 0)
.with_label(label)
.with_align(Align::Left | Align::Inside);
b.set_down_frame(FrameType::FlatBox);
b.set_selection_color(Color::color_average(b.color(), Color::Foreground, 0.9));
b.clear_visible_focus();
b.set_label_size(app::font_size() - 2);
b.draw(move |b| {
if b.value() {
let mut image = if (*ord.lock() as i32) < 5 {
image::SvgImage::from_data(styles::svgs::DESC).unwrap()
} else {
image::SvgImage::from_data(styles::svgs::ASC).unwrap()
};
image.scale(15, 15, true, true);
image.draw(b.x() + (b.w() * 2 / 3) + 5, b.y() + 10, b.w() / 3, b.h());
}
});
b.set_frame(FrameType::FlatBox);
Self { b }
}
}
fltk::widget_extends!(ProcToggle, button::RadioButton, b);
struct Proc {
pub pid: sysinfo::Pid,
pub memory: u64,
pub virt: u64,
pub cpu: f32,
pub exe: String,
pub user: String,
}
impl Proc {
pub fn new(pid: &sysinfo::Pid, proc: &sysinfo::Process, users: &Users) -> Self {
let user = proc
.user_id()
.and_then(|uid| users.get_user_by_id(uid))
.map(|u| u.name().to_string())
.unwrap_or_else(|| "<unknown>".to_string());
Self {
pid: *pid,
memory: proc.memory(),
virt: proc.virtual_memory(),
cpu: proc.cpu_usage(),
exe: proc.name().to_string_lossy().into_owned(),
user,
}
}
pub fn fmt(&self, light: bool) -> String {
if !light {
format!(
"@C255 {}\t@C255 {:.01}\t@C255 {:.01}\t@C255 {:.01}\t@C255{}\t@C255{}",
self.pid,
self.memory as f64 / 2_f64.powf(20.),
self.virt as f64 / 2_f64.powf(20.),
self.cpu,
self.user,
self.exe,
)
} else {
format!(
" {}\t {:.01}\t {:.01}\t {:.01}\t{}\t{}",
self.pid,
self.memory as f64 / 2_f64.powf(20.),
self.virt as f64 / 2_f64.powf(20.),
self.cpu,
self.user,
self.exe,
)
}
}
}
pub fn procs(view: &MyView) -> Option<Box<dyn FnMut() + Send>> {
let mut ord = view.ordering.lock();
*ord = SortOrder::Pid;
drop(ord);
let mut sys = view.system.lock();
sys.refresh_processes(ProcessesToUpdate::All, true);
let hpack = group::Pack::default().with_type(group::PackType::Horizontal);
let mut parent = group::Flex::from_dyn_widget(&hpack.parent().unwrap()).unwrap();
parent.fixed(&hpack, 30);
let mut b = ProcToggle::new("pid", view.ordering.clone());
b.set_value(true);
b.handle({
let ord = view.ordering.clone();
move |_, e| {
if e == Event::Push {
let mut ord = ord.lock();
if *ord == SortOrder::Pid {
*ord = SortOrder::RevPid;
} else {
*ord = SortOrder::Pid;
}
true
} else {
false
}
}
});
ProcToggle::new("mem%", view.ordering.clone()).handle({
let ord = view.ordering.clone();
move |_, e| {
if e == Event::Push {
let mut ord = ord.lock();
if *ord == SortOrder::Mem {
*ord = SortOrder::RevMem;
} else {
*ord = SortOrder::Mem;
}
true
} else {
false
}
}
});
let mut b = ProcToggle::new("virt", view.ordering.clone());
b.handle({
let ord = view.ordering.clone();
move |_, e| {
if e == Event::Push {
let mut ord = ord.lock();
if *ord == SortOrder::Virt {
*ord = SortOrder::RevVirt;
} else {
*ord = SortOrder::Virt;
}
true
} else {
false
}
}
});
b.set_tooltip("Virtual memory in Kb");
ProcToggle::new("cpu%", view.ordering.clone()).handle({
let ord = view.ordering.clone();
move |_, e| {
if e == Event::Push {
let mut ord = ord.lock();
if *ord == SortOrder::Cpu {
*ord = SortOrder::RevCpu;
} else {
*ord = SortOrder::Cpu;
}
true
} else {
false
}
}
});
ProcToggle::new("user", view.ordering.clone()).handle({
let ord = view.ordering.clone();
move |_, e| {
if e == Event::Push {
let mut ord = ord.lock();
if *ord == SortOrder::User {
*ord = SortOrder::RevUser;
} else {
*ord = SortOrder::User;
}
true
} else {
false
}
}
});
ProcToggle::new("\t\texe", view.ordering.clone()).handle({
let ord = view.ordering.clone();
move |_, e| {
if e == Event::Push {
let mut ord = ord.lock();
if *ord == SortOrder::Exe {
*ord = SortOrder::RevExe;
} else {
*ord = SortOrder::Exe;
}
true
} else {
false
}
}
});
hpack.end();
let mut grp = group::Group::default();
let mut b = browser::HoldBrowser::default();
b.clear_visible_focus();
b.set_text_size(app::font_size() - 2);
b.set_color(Color::color_average(b.color(), Color::Background, 0.1));
b.set_selection_color(SEL_BLUE);
b.set_scrollbar_size(5);
b.scrollbar()
.set_selection_color(Color::color_average(b.color(), Color::Foreground, 0.9));
b.hscrollbar()
.set_selection_color(Color::color_average(b.color(), Color::Foreground, 0.9));
b.set_frame(FrameType::GtkDownBox);
let widths = &[70, 70, 70, 70, 120, 70];
b.set_column_widths(widths);
b.set_column_char('\t');
let users = Users::new_with_refreshed_list();
let mut ps = vec![];
for (pid, process) in sys.processes() {
ps.push(Proc::new(pid, process, &users));
}
ps.sort_by(|p1, p2| p1.pid.cmp(&p2.pid));
let light_mode = view.light_mode.load(Ordering::Relaxed);
for p in ps {
b.add(&p.fmt(light_mode));
}
let mut menu = menu::MenuButton::default().with_type(menu::MenuButtonType::Popup3);
menu.set_frame(FrameType::FlatBox);
menu.set_text_size(app::font_size() - 2);
menu.set_color(Color::color_average(menu.color(), Color::Background, 0.9));
drop(sys);
menu.add_choice("End Task\t\t");
b.set_callback({
let menu = menu.clone();
move |_| {
if app::event_mouse_button() == MouseButton::Right {
menu.popup();
}
}
});
grp.end();
let mut row = group::Flex::default().row();
parent.fixed(&row, 30);
frame::Frame::default();
let mut btn = button::Button::default().with_label("End task");
btn.set_frame(FrameType::RFlatBox);
btn.set_color(BLUE);
btn.set_selection_color(SEL_BLUE);
btn.clear_visible_focus();
btn.set_callback({
let sys = view.system.clone();
let b = b.clone();
move |_| {
let val = b.value();
if val != 0 {
if let Some(text) = b.text(val) {
let sys = sys.lock();
let v: Vec<&str> = text.split_ascii_whitespace().collect();
let pid = if light_mode { v[0] } else { v[1] };
let pid = Pid::from_str(pid).unwrap();
if let Some(p) = sys.process(pid) {
let _ = p.kill_with(Signal::Kill);
}
drop(sys);
}
}
}
});
frame::Frame::default();
row.fixed(&btn, 80);
row.end();
menu.set_callback(move |m| {
if let Some(v) = m.choice() {
if v == "End Task" {
btn.do_callback();
}
}
});
grp.resize_callback({
let mut b = b.clone();
move |_, x, y, w, h| {
b.resize(x, y, w, h);
menu.resize(x, y, w, h);
}
});
let sys = Arc::new(Mutex::new(System::new_all()));
let users = Arc::new(users);
let light_mode = view.light_mode.clone();
let ord = view.ordering.clone();
let cb = move || {
if let Some(mut sys) = sys.try_lock() {
sys.refresh_processes(ProcessesToUpdate::All, true);
let mut ps = vec![];
for (pid, process) in sys.processes() {
ps.push(Proc::new(pid, process, &users));
}
ps.sort_by(|p1, p2| match *ord.lock() {
SortOrder::Pid => p1.pid.cmp(&p2.pid),
SortOrder::Mem => p1.memory.cmp(&p2.memory),
SortOrder::Virt => p1.virt.cmp(&p2.virt),
SortOrder::Cpu => p1.cpu.partial_cmp(&p2.cpu).unwrap(),
SortOrder::Exe => p1.exe.cmp(&p2.exe),
SortOrder::User => p1.user.cmp(&p2.user),
SortOrder::RevPid => p2.pid.cmp(&p1.pid),
SortOrder::RevMem => p2.memory.cmp(&p1.memory),
SortOrder::RevVirt => p2.virt.cmp(&p1.virt),
SortOrder::RevCpu => p2.cpu.partial_cmp(&p1.cpu).unwrap(),
SortOrder::RevExe => p2.exe.cmp(&p1.exe),
SortOrder::RevUser => p2.user.cmp(&p1.user),
});
let light_mode = light_mode.load(Ordering::Relaxed);
for (i, p) in ps.iter().enumerate() {
b.set_text(i as i32 + 1, &p.fmt(light_mode));
}
app::awake();
}
};
Some(Box::new(cb))
}