From 0bf7f32473a86bd4bb029b5dd61a3cb1d19a31ca Mon Sep 17 00:00:00 2001 From: ClementTsang Date: Sat, 1 Feb 2020 23:49:44 -0500 Subject: [PATCH] Optimizing processes... --- README.md | 4 +- src/app.rs | 186 +++++++++++++--------------- src/app/data_farmer.rs | 23 ++-- src/app/data_harvester.rs | 3 - src/app/data_harvester/processes.rs | 56 --------- src/canvas.rs | 100 +++++++-------- src/data_conversion.rs | 157 ++++++++--------------- src/main.rs | 158 ++++++++++++----------- src/utils/gen_util.rs | 26 ++++ 9 files changed, 305 insertions(+), 408 deletions(-) diff --git a/README.md b/README.md index 0732cb5e..2b877a6f 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,9 @@ Run using `btm`. - `-g`, `--group` will group together processes with the same name by default (equivalent to pressing `Tab`). -- `-i`, `--case_insensitive` will default to not matching case when searching processes. +- `-i`, `--case_insensitive` will default to not matching case + +when searching processes. ### Keybindings diff --git a/src/app.rs b/src/app.rs index 5b398c93..c1682728 100644 --- a/src/app.rs +++ b/src/app.rs @@ -5,12 +5,12 @@ use std::time::Instant; pub mod data_farmer; use data_farmer::*; -use crate::{canvas, constants, data_conversion::ConvertedProcessHarvest, utils::error::Result}; +use crate::{canvas, constants, data_conversion::ConvertedProcessData, utils::error::Result}; mod process_killer; #[derive(Clone, Copy)] -pub enum ApplicationPosition { +pub enum WidgetPosition { Cpu, Mem, Disk, @@ -68,7 +68,7 @@ pub struct App { pub temperature_type: temperature::TemperatureType, pub update_rate_in_milliseconds: u64, pub show_average_cpu: bool, - pub current_application_position: ApplicationPosition, + pub current_widget_selected: WidgetPosition, pub data: data_harvester::Data, awaiting_second_char: bool, second_char: char, @@ -76,12 +76,12 @@ pub struct App { pub show_help: bool, pub show_dd: bool, pub dd_err: Option, - to_delete_process_list: Option>, + to_delete_process_list: Option>, pub is_frozen: bool, pub left_legend: bool, pub use_current_cpu_total: bool, last_key_press: Instant, - pub canvas_data: canvas::CanvasData, + pub canvas_data: canvas::DisplayableData, enable_grouping: bool, enable_searching: bool, current_search_query: String, @@ -105,7 +105,7 @@ impl App { temperature_type, update_rate_in_milliseconds, show_average_cpu, - current_application_position: ApplicationPosition::Process, + current_widget_selected: WidgetPosition::Process, scroll_direction: ScrollDirection::DOWN, currently_selected_process_position: 0, currently_selected_disk_position: 0, @@ -127,7 +127,7 @@ impl App { left_legend, use_current_cpu_total, last_key_press: Instant::now(), - canvas_data: canvas::CanvasData::default(), + canvas_data: canvas::DisplayableData::default(), enable_grouping: false, enable_searching: false, current_search_query: String::default(), @@ -144,7 +144,7 @@ impl App { self.show_help = false; self.show_dd = false; if self.enable_searching { - self.current_application_position = ApplicationPosition::Process; + self.current_widget_selected = WidgetPosition::Process; self.enable_searching = false; } self.current_search_query = String::new(); @@ -161,7 +161,7 @@ impl App { self.to_delete_process_list = None; self.dd_err = None; } else if self.enable_searching { - self.current_application_position = ApplicationPosition::Process; + self.current_widget_selected = WidgetPosition::Process; self.enable_searching = false; } } @@ -178,16 +178,17 @@ impl App { pub fn toggle_grouping(&mut self) { // Disallow usage whilst in a dialog and only in processes if !self.is_in_dialog() { - if let ApplicationPosition::Process = self.current_application_position { + if let WidgetPosition::Process = self.current_widget_selected { self.enable_grouping = !(self.enable_grouping); + self.update_process_gui = true; } } } pub fn on_tab(&mut self) { - match self.current_application_position { - ApplicationPosition::Process => self.toggle_grouping(), - ApplicationPosition::Disk => {} + match self.current_widget_selected { + WidgetPosition::Process => self.toggle_grouping(), + WidgetPosition::Disk => {} _ => {} } } @@ -198,11 +199,11 @@ impl App { pub fn enable_searching(&mut self) { if !self.is_in_dialog() { - match self.current_application_position { - ApplicationPosition::Process | ApplicationPosition::ProcessSearch => { + match self.current_widget_selected { + WidgetPosition::Process | WidgetPosition::ProcessSearch => { // Toggle on self.enable_searching = true; - self.current_application_position = ApplicationPosition::ProcessSearch; + self.current_widget_selected = WidgetPosition::ProcessSearch; } _ => {} } @@ -214,7 +215,7 @@ impl App { } pub fn is_in_search_widget(&self) -> bool { - if let ApplicationPosition::ProcessSearch = self.current_application_position { + if let WidgetPosition::ProcessSearch = self.current_widget_selected { true } else { false @@ -223,7 +224,7 @@ impl App { pub fn search_with_pid(&mut self) { if !self.is_in_dialog() && self.is_searching() { - if let ApplicationPosition::ProcessSearch = self.current_application_position { + if let WidgetPosition::ProcessSearch = self.current_widget_selected { self.searching_pid = true; } } @@ -231,7 +232,7 @@ impl App { pub fn search_with_name(&mut self) { if !self.is_in_dialog() && self.is_searching() { - if let ApplicationPosition::ProcessSearch = self.current_application_position { + if let WidgetPosition::ProcessSearch = self.current_widget_selected { self.searching_pid = false; } } @@ -247,7 +248,7 @@ impl App { pub fn toggle_simple_search(&mut self) { if !self.is_in_dialog() && self.is_searching() { - if let ApplicationPosition::ProcessSearch = self.current_application_position { + if let WidgetPosition::ProcessSearch = self.current_widget_selected { self.use_simple = !self.use_simple; // Update to latest (when simple is on this is not updated) @@ -287,7 +288,7 @@ impl App { } pub fn on_backspace(&mut self) { - if let ApplicationPosition::ProcessSearch = self.current_application_position { + if let WidgetPosition::ProcessSearch = self.current_widget_selected { if self.current_cursor_position > 0 { self.current_cursor_position -= 1; self.current_search_query @@ -311,7 +312,7 @@ impl App { pub fn on_up_key(&mut self) { if !self.is_in_dialog() { - if let ApplicationPosition::ProcessSearch = self.current_application_position { + if let WidgetPosition::ProcessSearch = self.current_widget_selected { } else { self.decrement_position_count(); } @@ -320,7 +321,7 @@ impl App { pub fn on_down_key(&mut self) { if !self.is_in_dialog() { - if let ApplicationPosition::ProcessSearch = self.current_application_position { + if let WidgetPosition::ProcessSearch = self.current_widget_selected { } else { self.increment_position_count(); } @@ -329,7 +330,7 @@ impl App { pub fn on_left_key(&mut self) { if !self.is_in_dialog() { - if let ApplicationPosition::ProcessSearch = self.current_application_position { + if let WidgetPosition::ProcessSearch = self.current_widget_selected { if self.current_cursor_position > 0 { self.current_cursor_position -= 1; } @@ -339,7 +340,7 @@ impl App { pub fn on_right_key(&mut self) { if !self.is_in_dialog() { - if let ApplicationPosition::ProcessSearch = self.current_application_position { + if let WidgetPosition::ProcessSearch = self.current_widget_selected { if self.current_cursor_position < self.current_search_query.len() { self.current_cursor_position += 1; } @@ -349,7 +350,7 @@ impl App { pub fn skip_cursor_beginning(&mut self) { if !self.is_in_dialog() { - if let ApplicationPosition::ProcessSearch = self.current_application_position { + if let WidgetPosition::ProcessSearch = self.current_widget_selected { self.current_cursor_position = 0; } } @@ -357,7 +358,7 @@ impl App { pub fn skip_cursor_end(&mut self) { if !self.is_in_dialog() { - if let ApplicationPosition::ProcessSearch = self.current_application_position { + if let WidgetPosition::ProcessSearch = self.current_widget_selected { self.current_cursor_position = self.current_search_query.len(); } } @@ -375,7 +376,7 @@ impl App { } self.last_key_press = current_key_press_inst; - if let ApplicationPosition::ProcessSearch = self.current_application_position { + if let WidgetPosition::ProcessSearch = self.current_widget_selected { self.current_search_query .insert(self.current_cursor_position, caught_char); self.current_cursor_position += 1; @@ -394,31 +395,14 @@ impl App { self.enable_searching(); } 'd' => { - if let ApplicationPosition::Process = self.current_application_position { + if let WidgetPosition::Process = self.current_widget_selected { if self.awaiting_second_char && self.second_char == 'd' { self.awaiting_second_char = false; self.second_char = ' '; - let current_process = if self.is_grouped() { - let mut res: Vec = Vec::new(); - for pid in &self.canvas_data.grouped_process_data - [self.currently_selected_process_position as usize] - .group - { - let result = self - .canvas_data - .process_data - .iter() - .find(|p| p.pid == *pid); - if let Some(process) = result { - res.push((*process).clone()); - } - } - res - } else { - vec![self.canvas_data.process_data - [self.currently_selected_process_position as usize] - .clone()] - }; + let current_process = Vec::new(); + + // TODO: FIX THIS SHITTTTTT + self.to_delete_process_list = Some(current_process); self.show_dd = true; self.reset_multi_tap_keys(); @@ -513,7 +497,7 @@ impl App { pub fn kill_highlighted_process(&mut self) -> Result<()> { // Technically unnecessary but this is a good check... - if let ApplicationPosition::Process = self.current_application_position { + if let WidgetPosition::Process = self.current_widget_selected { if let Some(current_selected_processes) = &(self.to_delete_process_list) { for current_selected_process in current_selected_processes { process_killer::kill_process_given_pid(current_selected_process.pid)?; @@ -524,7 +508,7 @@ impl App { Ok(()) } - pub fn get_current_highlighted_process_list(&self) -> Option> { + pub fn get_current_highlighted_process_list(&self) -> Option> { self.to_delete_process_list.clone() } @@ -540,12 +524,12 @@ impl App { // PROC_SEARCH -(up)> Disk, -(down)> PROC, -(left)> Network pub fn move_left(&mut self) { if !self.is_in_dialog() { - self.current_application_position = match self.current_application_position { - ApplicationPosition::Process => ApplicationPosition::Network, - ApplicationPosition::ProcessSearch => ApplicationPosition::Network, - ApplicationPosition::Disk => ApplicationPosition::Mem, - ApplicationPosition::Temp => ApplicationPosition::Mem, - _ => self.current_application_position, + self.current_widget_selected = match self.current_widget_selected { + WidgetPosition::Process => WidgetPosition::Network, + WidgetPosition::ProcessSearch => WidgetPosition::Network, + WidgetPosition::Disk => WidgetPosition::Mem, + WidgetPosition::Temp => WidgetPosition::Mem, + _ => self.current_widget_selected, }; self.reset_multi_tap_keys(); } @@ -553,10 +537,10 @@ impl App { pub fn move_right(&mut self) { if !self.is_in_dialog() { - self.current_application_position = match self.current_application_position { - ApplicationPosition::Mem => ApplicationPosition::Temp, - ApplicationPosition::Network => ApplicationPosition::Process, - _ => self.current_application_position, + self.current_widget_selected = match self.current_widget_selected { + WidgetPosition::Mem => WidgetPosition::Temp, + WidgetPosition::Network => WidgetPosition::Process, + _ => self.current_widget_selected, }; self.reset_multi_tap_keys(); } @@ -564,20 +548,20 @@ impl App { pub fn move_up(&mut self) { if !self.is_in_dialog() { - self.current_application_position = match self.current_application_position { - ApplicationPosition::Mem => ApplicationPosition::Cpu, - ApplicationPosition::Network => ApplicationPosition::Mem, - ApplicationPosition::Process => { + self.current_widget_selected = match self.current_widget_selected { + WidgetPosition::Mem => WidgetPosition::Cpu, + WidgetPosition::Network => WidgetPosition::Mem, + WidgetPosition::Process => { if self.is_searching() { - ApplicationPosition::ProcessSearch + WidgetPosition::ProcessSearch } else { - ApplicationPosition::Disk + WidgetPosition::Disk } } - ApplicationPosition::ProcessSearch => ApplicationPosition::Disk, - ApplicationPosition::Temp => ApplicationPosition::Cpu, - ApplicationPosition::Disk => ApplicationPosition::Temp, - _ => self.current_application_position, + WidgetPosition::ProcessSearch => WidgetPosition::Disk, + WidgetPosition::Temp => WidgetPosition::Cpu, + WidgetPosition::Disk => WidgetPosition::Temp, + _ => self.current_widget_selected, }; self.reset_multi_tap_keys(); } @@ -585,19 +569,19 @@ impl App { pub fn move_down(&mut self) { if !self.is_in_dialog() { - self.current_application_position = match self.current_application_position { - ApplicationPosition::Cpu => ApplicationPosition::Mem, - ApplicationPosition::Mem => ApplicationPosition::Network, - ApplicationPosition::Temp => ApplicationPosition::Disk, - ApplicationPosition::Disk => { + self.current_widget_selected = match self.current_widget_selected { + WidgetPosition::Cpu => WidgetPosition::Mem, + WidgetPosition::Mem => WidgetPosition::Network, + WidgetPosition::Temp => WidgetPosition::Disk, + WidgetPosition::Disk => { if self.is_searching() { - ApplicationPosition::ProcessSearch + WidgetPosition::ProcessSearch } else { - ApplicationPosition::Process + WidgetPosition::Process } } - ApplicationPosition::ProcessSearch => ApplicationPosition::Process, - _ => self.current_application_position, + WidgetPosition::ProcessSearch => WidgetPosition::Process, + _ => self.current_widget_selected, }; self.reset_multi_tap_keys(); } @@ -605,11 +589,11 @@ impl App { pub fn skip_to_first(&mut self) { if !self.is_in_dialog() { - match self.current_application_position { - ApplicationPosition::Process => self.currently_selected_process_position = 0, - ApplicationPosition::Temp => self.currently_selected_temperature_position = 0, - ApplicationPosition::Disk => self.currently_selected_disk_position = 0, - ApplicationPosition::Cpu => self.currently_selected_cpu_table_position = 0, + match self.current_widget_selected { + WidgetPosition::Process => self.currently_selected_process_position = 0, + WidgetPosition::Temp => self.currently_selected_temperature_position = 0, + WidgetPosition::Disk => self.currently_selected_disk_position = 0, + WidgetPosition::Cpu => self.currently_selected_cpu_table_position = 0, _ => {} } @@ -620,19 +604,19 @@ impl App { pub fn skip_to_last(&mut self) { if !self.is_in_dialog() { - match self.current_application_position { - ApplicationPosition::Process => { + match self.current_widget_selected { + WidgetPosition::Process => { self.currently_selected_process_position = self.data.list_of_processes.len() as i64 - 1 } - ApplicationPosition::Temp => { + WidgetPosition::Temp => { self.currently_selected_temperature_position = self.data.temperature_sensors.len() as i64 - 1 } - ApplicationPosition::Disk => { + WidgetPosition::Disk => { self.currently_selected_disk_position = self.data.disks.len() as i64 - 1 } - ApplicationPosition::Cpu => { + WidgetPosition::Cpu => { self.currently_selected_cpu_table_position = self.canvas_data.cpu_data.len() as i64 - 1; } @@ -645,11 +629,11 @@ impl App { pub fn decrement_position_count(&mut self) { if !self.is_in_dialog() { - match self.current_application_position { - ApplicationPosition::Process => self.change_process_position(-1), - ApplicationPosition::Temp => self.change_temp_position(-1), - ApplicationPosition::Disk => self.change_disk_position(-1), - ApplicationPosition::Cpu => self.change_cpu_table_position(-1), // TODO: Temporary, may change if we add scaling + match self.current_widget_selected { + WidgetPosition::Process => self.change_process_position(-1), + WidgetPosition::Temp => self.change_temp_position(-1), + WidgetPosition::Disk => self.change_disk_position(-1), + WidgetPosition::Cpu => self.change_cpu_table_position(-1), // TODO: Temporary, may change if we add scaling _ => {} } self.scroll_direction = ScrollDirection::UP; @@ -659,11 +643,11 @@ impl App { pub fn increment_position_count(&mut self) { if !self.is_in_dialog() { - match self.current_application_position { - ApplicationPosition::Process => self.change_process_position(1), - ApplicationPosition::Temp => self.change_temp_position(1), - ApplicationPosition::Disk => self.change_disk_position(1), - ApplicationPosition::Cpu => self.change_cpu_table_position(1), // TODO: Temporary, may change if we add scaling + match self.current_widget_selected { + WidgetPosition::Process => self.change_process_position(1), + WidgetPosition::Temp => self.change_temp_position(1), + WidgetPosition::Disk => self.change_disk_position(1), + WidgetPosition::Cpu => self.change_cpu_table_position(1), // TODO: Temporary, may change if we add scaling _ => {} } self.scroll_direction = ScrollDirection::DOWN; diff --git a/src/app/data_farmer.rs b/src/app/data_farmer.rs index f370aefc..4793a109 100644 --- a/src/app/data_farmer.rs +++ b/src/app/data_farmer.rs @@ -49,7 +49,7 @@ pub struct DataCollection { pub memory_harvest: mem::MemHarvest, pub swap_harvest: mem::MemHarvest, pub cpu_harvest: cpu::CPUHarvest, - pub process_harvest: processes::ProcessHarvest, + pub process_harvest: Vec, pub disk_harvest: Vec, pub io_harvest: disks::IOHarvest, pub io_labels: Vec<(u64, u64)>, @@ -66,7 +66,7 @@ impl Default for DataCollection { memory_harvest: mem::MemHarvest::default(), swap_harvest: mem::MemHarvest::default(), cpu_harvest: cpu::CPUHarvest::default(), - process_harvest: processes::ProcessHarvest::default(), + process_harvest: Vec::default(), disk_harvest: Vec::default(), io_harvest: disks::IOHarvest::default(), io_labels: Vec::default(), @@ -106,14 +106,15 @@ impl DataCollection { self.eat_cpu(&harvested_data, &harvested_time, &mut new_entry); // Temp - self.eat_temp(&harvested_data, &harvested_time, &mut new_entry); + self.eat_temp(&harvested_data); // Disks - self.eat_disks(&harvested_data, &harvested_time, &mut new_entry); + self.eat_disks(&harvested_data, &harvested_time); // Processes + self.eat_proc(&harvested_data); - // And we're done eating. + // And we're done eating. Update time and push the new entry! self.current_instant = harvested_time; self.timed_data_vec.push((harvested_time, new_entry)); } @@ -212,16 +213,12 @@ impl DataCollection { self.cpu_harvest = harvested_data.cpu.clone(); } - fn eat_temp( - &mut self, harvested_data: &Data, _harvested_time: &Instant, _new_entry: &mut TimedData, - ) { + fn eat_temp(&mut self, harvested_data: &Data) { // TODO: [PO] To implement self.temp_harvest = harvested_data.temperature_sensors.clone(); } - fn eat_disks( - &mut self, harvested_data: &Data, harvested_time: &Instant, _new_entry: &mut TimedData, - ) { + fn eat_disks(&mut self, harvested_data: &Data, harvested_time: &Instant) { // TODO: [PO] To implement let time_since_last_harvest = harvested_time @@ -256,6 +253,10 @@ impl DataCollection { self.disk_harvest = harvested_data.disks.clone(); self.io_harvest = harvested_data.io.clone(); } + + fn eat_proc(&mut self, harvested_data: &Data) { + self.process_harvest = harvested_data.list_of_processes.clone(); + } } pub fn generate_joining_points( diff --git a/src/app/data_harvester.rs b/src/app/data_harvester.rs index b40380c9..9ad176b5 100644 --- a/src/app/data_harvester.rs +++ b/src/app/data_harvester.rs @@ -25,7 +25,6 @@ pub struct Data { pub temperature_sensors: Vec, pub network: network::NetworkHarvest, pub list_of_processes: Vec, - pub grouped_list_of_processes: Option>, pub disks: Vec, pub io: disks::IOHarvest, pub last_collection_time: Instant, @@ -39,7 +38,6 @@ impl Default for Data { swap: mem::MemHarvest::default(), temperature_sensors: Vec::default(), list_of_processes: Vec::default(), - grouped_list_of_processes: None, disks: Vec::default(), io: disks::IOHarvest::default(), network: network::NetworkHarvest::default(), @@ -53,7 +51,6 @@ impl Data { self.io = disks::IOHarvest::default(); self.temperature_sensors = Vec::new(); self.list_of_processes = Vec::new(); - self.grouped_list_of_processes = None; self.disks = Vec::new(); self.network.first_run_cleanup(); diff --git a/src/app/data_harvester/processes.rs b/src/app/data_harvester/processes.rs index 8f24b94d..0b8371fe 100644 --- a/src/app/data_harvester/processes.rs +++ b/src/app/data_harvester/processes.rs @@ -1,5 +1,4 @@ use crate::utils::error; -use std::cmp::Ordering; use std::{collections::HashMap, process::Command, time::Instant}; use sysinfo::{ProcessExt, System, SystemExt}; @@ -23,7 +22,6 @@ pub struct ProcessHarvest { pub cpu_usage_percent: f64, pub mem_usage_percent: f64, pub name: String, - pub pid_vec: Option>, } fn cpu_usage_calculation( @@ -100,31 +98,6 @@ fn cpu_usage_calculation( Ok((result, cpu_percentage)) } -fn get_ordering( - a_val: T, b_val: T, reverse_order: bool, -) -> std::cmp::Ordering { - match a_val.partial_cmp(&b_val) { - Some(x) => match x { - Ordering::Greater => { - if reverse_order { - std::cmp::Ordering::Less - } else { - std::cmp::Ordering::Greater - } - } - Ordering::Less => { - if reverse_order { - std::cmp::Ordering::Greater - } else { - std::cmp::Ordering::Less - } - } - Ordering::Equal => Ordering::Equal, - }, - None => Ordering::Equal, - } -} - fn get_process_cpu_stats(pid: u32) -> std::io::Result { let mut path = std::path::PathBuf::new(); path.push("/proc"); @@ -188,7 +161,6 @@ fn convert_ps( name: "".to_string(), mem_usage_percent: 0.0, cpu_usage_percent: 0.0, - pid_vec: None, }); } @@ -217,7 +189,6 @@ fn convert_ps( use_current_cpu_total, curr_time, )?, - pid_vec: None, }) } @@ -292,36 +263,9 @@ pub fn get_sorted_processes_list( name, mem_usage_percent: process_val.memory() as f64 * 100.0 / mem_total_kb as f64, cpu_usage_percent: f64::from(process_val.cpu_usage()), - pid_vec: None, }); } } Ok(process_vector) } - -pub fn sort_processes( - process_vector: &mut Vec, sorting_method: &ProcessSorting, reverse_order: bool, -) { - // Always sort alphabetically first! - process_vector.sort_by(|a, b| get_ordering(&a.name, &b.name, false)); - - match sorting_method { - ProcessSorting::CPU => { - process_vector.sort_by(|a, b| { - get_ordering(a.cpu_usage_percent, b.cpu_usage_percent, reverse_order) - }); - } - ProcessSorting::MEM => { - process_vector.sort_by(|a, b| { - get_ordering(a.mem_usage_percent, b.mem_usage_percent, reverse_order) - }); - } - ProcessSorting::PID => { - process_vector.sort_by(|a, b| get_ordering(a.pid, b.pid, reverse_order)); - } - ProcessSorting::NAME => { - process_vector.sort_by(|a, b| get_ordering(&a.name, &b.name, reverse_order)) - } - } -} diff --git a/src/canvas.rs b/src/canvas.rs index 6011c14b..093e6815 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -1,9 +1,11 @@ use crate::{ - app, constants, - data_conversion::{ConvertedCpuData, ConvertedProcessHarvest}, + app::{self, data_harvester::processes::ProcessHarvest}, + constants, + data_conversion::{ConvertedCpuData, ConvertedProcessData}, utils::{error, gen_util::*}, }; use std::cmp::max; +use std::collections::HashMap; use tui::{ backend, layout::{Alignment, Constraint, Direction, Layout, Rect}, @@ -85,7 +87,7 @@ lazy_static! { } #[derive(Default)] -pub struct CanvasData { +pub struct DisplayableData { pub rx_display: String, pub tx_display: String, pub total_rx_display: String, @@ -94,8 +96,9 @@ pub struct CanvasData { pub network_data_tx: Vec<(f64, f64)>, pub disk_data: Vec>, pub temp_sensor_data: Vec>, - pub process_data: Vec, - pub grouped_process_data: Vec, + pub process_data: HashMap, // Not final + pub grouped_process_data: Vec, // Not final + pub finalized_process_data: Vec, // What's actually displayed pub mem_label: String, pub swap_label: String, pub mem_data: Vec<(f64, f64)>, @@ -441,8 +444,8 @@ fn draw_cpu_graph(f: &mut Frame, app_state: &app::App, d Block::default() .title("CPU") .borders(Borders::ALL) - .border_style(match app_state.current_application_position { - app::ApplicationPosition::Cpu => *CANVAS_HIGHLIGHTED_BORDER_STYLE, + .border_style(match app_state.current_widget_selected { + app::WidgetPosition::Cpu => *CANVAS_HIGHLIGHTED_BORDER_STYLE, _ => *CANVAS_BORDER_STYLE, }), ) @@ -485,8 +488,8 @@ fn draw_cpu_legend( .map(|(itx, cpu_string_row)| { Row::StyledData( cpu_string_row.iter(), - match app_state.current_application_position { - app::ApplicationPosition::Cpu => { + match app_state.current_widget_selected { + app::WidgetPosition::Cpu => { if cpu_row_counter == app_state.currently_selected_cpu_table_position - start_position { @@ -515,8 +518,8 @@ fn draw_cpu_legend( // Draw Table::new(CPU_LEGEND_HEADER.iter(), cpu_rows) .block(Block::default().borders(Borders::ALL).border_style( - match app_state.current_application_position { - app::ApplicationPosition::Cpu => *CANVAS_HIGHLIGHTED_BORDER_STYLE, + match app_state.current_widget_selected { + app::WidgetPosition::Cpu => *CANVAS_HIGHLIGHTED_BORDER_STYLE, _ => *CANVAS_BORDER_STYLE, }, )) @@ -554,8 +557,8 @@ fn draw_memory_table( // Draw Table::new(MEM_HEADERS.iter(), mapped_mem_rows) .block(Block::default().borders(Borders::ALL).border_style( - match app_state.current_application_position { - app::ApplicationPosition::Mem => *CANVAS_HIGHLIGHTED_BORDER_STYLE, + match app_state.current_widget_selected { + app::WidgetPosition::Mem => *CANVAS_HIGHLIGHTED_BORDER_STYLE, _ => *CANVAS_BORDER_STYLE, }, )) @@ -576,9 +579,11 @@ fn draw_memory_graph(f: &mut Frame, app_state: &app::App let x_axis: Axis = Axis::default() .style(Style::default().fg(GRAPH_COLOUR)) .bounds([0.0, constants::TIME_STARTS_FROM as f64]); - let y_axis = Axis::default() + + // Offset as the zero value isn't drawn otherwise... + let y_axis: Axis<&str> = Axis::default() .style(Style::default().fg(GRAPH_COLOUR)) - .bounds([-0.5, 100.5]) // Offset as the zero value isn't drawn otherwise... + .bounds([-0.5, 100.5]) .labels(&["0%", "100%"]); let mut mem_canvas_vec: Vec = vec![Dataset::default() @@ -610,8 +615,8 @@ fn draw_memory_graph(f: &mut Frame, app_state: &app::App Block::default() .title("Memory") .borders(Borders::ALL) - .border_style(match app_state.current_application_position { - app::ApplicationPosition::Mem => *CANVAS_HIGHLIGHTED_BORDER_STYLE, + .border_style(match app_state.current_widget_selected { + app::WidgetPosition::Mem => *CANVAS_HIGHLIGHTED_BORDER_STYLE, _ => *CANVAS_BORDER_STYLE, }), ) @@ -637,8 +642,8 @@ fn draw_network_graph(f: &mut Frame, app_state: &app::Ap Block::default() .title("Network") .borders(Borders::ALL) - .border_style(match app_state.current_application_position { - app::ApplicationPosition::Network => *CANVAS_HIGHLIGHTED_BORDER_STYLE, + .border_style(match app_state.current_widget_selected { + app::WidgetPosition::Network => *CANVAS_HIGHLIGHTED_BORDER_STYLE, _ => *CANVAS_BORDER_STYLE, }), ) @@ -715,8 +720,8 @@ fn draw_network_labels( mapped_network, ) .block(Block::default().borders(Borders::ALL).border_style( - match app_state.current_application_position { - app::ApplicationPosition::Network => *CANVAS_HIGHLIGHTED_BORDER_STYLE, + match app_state.current_widget_selected { + app::WidgetPosition::Network => *CANVAS_HIGHLIGHTED_BORDER_STYLE, _ => *CANVAS_BORDER_STYLE, }, )) @@ -749,8 +754,8 @@ fn draw_temp_table( let temperature_rows = sliced_vec.iter().map(|temp_row| { Row::StyledData( temp_row.iter(), - match app_state.current_application_position { - app::ApplicationPosition::Temp => { + match app_state.current_widget_selected { + app::WidgetPosition::Temp => { if temp_row_counter == app_state.currently_selected_temperature_position - start_position { @@ -782,8 +787,8 @@ fn draw_temp_table( Block::default() .title("Temperatures") .borders(Borders::ALL) - .border_style(match app_state.current_application_position { - app::ApplicationPosition::Temp => *CANVAS_HIGHLIGHTED_BORDER_STYLE, + .border_style(match app_state.current_widget_selected { + app::WidgetPosition::Temp => *CANVAS_HIGHLIGHTED_BORDER_STYLE, _ => *CANVAS_BORDER_STYLE, }), ) @@ -815,8 +820,8 @@ fn draw_disk_table( let disk_rows = sliced_vec.iter().map(|disk| { Row::StyledData( disk.iter(), - match app_state.current_application_position { - app::ApplicationPosition::Disk => { + match app_state.current_widget_selected { + app::WidgetPosition::Disk => { if disk_counter == app_state.currently_selected_disk_position - start_position { disk_counter = -1; Style::default().fg(Color::Black).bg(Color::Cyan) @@ -847,8 +852,8 @@ fn draw_disk_table( Block::default() .title("Disk") .borders(Borders::ALL) - .border_style(match app_state.current_application_position { - app::ApplicationPosition::Disk => *CANVAS_HIGHLIGHTED_BORDER_STYLE, + .border_style(match app_state.current_widget_selected { + app::WidgetPosition::Disk => *CANVAS_HIGHLIGHTED_BORDER_STYLE, _ => *CANVAS_BORDER_STYLE, }), ) @@ -881,8 +886,7 @@ fn draw_search_field( .chars() .enumerate() .map(|(itx, c)| { - if let app::ApplicationPosition::ProcessSearch = app_state.current_application_position - { + if let app::WidgetPosition::ProcessSearch = app_state.current_widget_selected { if itx == cursor_position { return Text::styled( c.to_string(), @@ -893,7 +897,7 @@ fn draw_search_field( Text::styled(c.to_string(), Style::default().fg(TEXT_COLOUR)) }) .collect::>(); - if let app::ApplicationPosition::ProcessSearch = app_state.current_application_position { + if let app::WidgetPosition::ProcessSearch = app_state.current_widget_selected { if cursor_position >= query.len() { query_with_cursor.push(Text::styled( " ".to_string(), @@ -926,8 +930,8 @@ fn draw_search_field( .border_style(if app_state.get_current_regex_matcher().is_err() { Style::default().fg(Color::Red) } else { - match app_state.current_application_position { - app::ApplicationPosition::ProcessSearch => *CANVAS_HIGHLIGHTED_BORDER_STYLE, + match app_state.current_widget_selected { + app::WidgetPosition::ProcessSearch => *CANVAS_HIGHLIGHTED_BORDER_STYLE, _ => *CANVAS_BORDER_STYLE, } }), @@ -941,16 +945,15 @@ fn draw_search_field( fn draw_processes_table( f: &mut Frame, app_state: &mut app::App, draw_loc: Rect, ) { - let process_data: &[ConvertedProcessHarvest] = if app_state.is_grouped() { - &app_state.canvas_data.grouped_process_data - } else { - &app_state.canvas_data.process_data - }; + let process_data: &[ConvertedProcessData] = &app_state.canvas_data.finalized_process_data; // Admittedly this is kinda a hack... but we need to: // * Scroll // * Show/hide elements based on scroll position - // As such, we use a process_counter to know when we've hit the process we've currently scrolled to. We also need to move the list - we can + // + // As such, we use a process_counter to know when we've + // hit the process we've currently scrolled to. + // We also need to move the list - we can // do so by hiding some elements! let num_rows = i64::from(draw_loc.height) - 5; @@ -961,26 +964,25 @@ fn draw_processes_table( app_state.currently_selected_process_position, ); - let sliced_vec: Vec = - (&process_data[start_position as usize..]).to_vec(); + let sliced_vec: Vec = (&process_data[start_position as usize..]).to_vec(); let mut process_counter = 0; // Draw! let process_rows = sliced_vec.iter().map(|process| { let stringified_process_vec: Vec = vec![ if app_state.is_grouped() { - process.group.len().to_string() + process.group_pids.len().to_string() } else { process.pid.to_string() }, process.name.clone(), - process.cpu_usage.clone(), - process.mem_usage.clone(), + format!("{:.1}%", process.cpu_usage), + format!("{:.1}%", process.mem_usage), ]; Row::StyledData( stringified_process_vec.into_iter(), - match app_state.current_application_position { - app::ApplicationPosition::Process => { + match app_state.current_widget_selected { + app::WidgetPosition::Process => { if process_counter == app_state.currently_selected_process_position - start_position { @@ -1042,8 +1044,8 @@ fn draw_processes_table( Block::default() .title("Processes") .borders(Borders::ALL) - .border_style(match app_state.current_application_position { - app::ApplicationPosition::Process => *CANVAS_HIGHLIGHTED_BORDER_STYLE, + .border_style(match app_state.current_widget_selected { + app::WidgetPosition::Process => *CANVAS_HIGHLIGHTED_BORDER_STYLE, _ => *CANVAS_BORDER_STYLE, }), ) diff --git a/src/data_conversion.rs b/src/data_conversion.rs index a01842f2..15234c11 100644 --- a/src/data_conversion.rs +++ b/src/data_conversion.rs @@ -2,14 +2,16 @@ //! can actually handle. use crate::{ - app::data_farmer, - app::data_harvester, - app::App, + app::{ + data_farmer, + data_harvester::{self, processes::ProcessHarvest}, + App, + }, constants, utils::gen_util::{get_exact_byte_values, get_simple_byte_values}, }; use constants::*; -use regex::Regex; +use std::collections::HashMap; #[derive(Default, Debug)] pub struct ConvertedNetworkData { @@ -22,12 +24,12 @@ pub struct ConvertedNetworkData { } #[derive(Clone, Default, Debug)] -pub struct ConvertedProcessHarvest { +pub struct ConvertedProcessData { pub pid: u32, pub name: String, - pub cpu_usage: String, - pub mem_usage: String, - pub group: Vec, + pub cpu_usage: f64, + pub mem_usage: f64, + pub group_pids: Vec, } #[derive(Clone, Default, Debug)] @@ -117,103 +119,6 @@ pub fn update_disk_row(current_data: &data_farmer::DataCollection) -> Vec (Vec, Vec) { - let process_vector: Vec = app_data - .list_of_processes - .iter() - .filter(|process| { - if use_pid { - process - .pid - .to_string() - .to_ascii_lowercase() - .contains(matching_string) - } else { - process.name.to_ascii_lowercase().contains(matching_string) - } - }) - .map(|process| return_mapped_process(process)) - .collect::>(); - - let mut grouped_process_vector: Vec = Vec::new(); - if let Some(grouped_list_of_processes) = &app_data.grouped_list_of_processes { - grouped_process_vector = grouped_list_of_processes - .iter() - .filter(|process| { - if use_pid { - process - .pid - .to_string() - .to_ascii_lowercase() - .contains(matching_string) - } else { - process.name.to_ascii_lowercase().contains(matching_string) - } - }) - .map(|process| return_mapped_process(process)) - .collect::>(); - } - - (process_vector, grouped_process_vector) -} - -pub fn regex_update_process_row( - app_data: &data_harvester::Data, regex_matcher: &std::result::Result, - use_pid: bool, -) -> (Vec, Vec) { - let process_vector: Vec = app_data - .list_of_processes - .iter() - .filter(|process| { - if let Ok(matcher) = regex_matcher { - if use_pid { - matcher.is_match(&process.pid.to_string()) - } else { - matcher.is_match(&process.name) - } - } else { - true - } - }) - .map(|process| return_mapped_process(process)) - .collect::>(); - - let mut grouped_process_vector: Vec = Vec::new(); - if let Some(grouped_list_of_processes) = &app_data.grouped_list_of_processes { - grouped_process_vector = grouped_list_of_processes - .iter() - .filter(|process| { - if let Ok(matcher) = regex_matcher { - if use_pid { - matcher.is_match(&process.pid.to_string()) - } else { - matcher.is_match(&process.name) - } - } else { - true - } - }) - .map(|process| return_mapped_process(process)) - .collect::>(); - } - - (process_vector, grouped_process_vector) -} - -fn return_mapped_process( - process: &data_harvester::processes::ProcessHarvest, -) -> ConvertedProcessHarvest { - ConvertedProcessHarvest { - pid: process.pid, - name: process.name.to_string(), - cpu_usage: format!("{:.1}%", process.cpu_usage_percent), - mem_usage: format!("{:.1}%", process.mem_usage_percent), - group: vec![], - } -} - pub fn update_cpu_data_points( show_avg_cpu: bool, current_data: &data_farmer::DataCollection, ) -> Vec { @@ -432,3 +337,45 @@ pub fn convert_network_data_points( total_tx_display, } } + +pub fn convert_process_data( + current_data: &data_farmer::DataCollection, +) -> (HashMap, Vec) { + let mut single_list = HashMap::new(); + + // cpu, mem, pids + let mut grouped_hashmap: HashMap)> = + std::collections::HashMap::new(); + + // Go through every single process in the list... and build a hashmap + single list + for process in &(current_data).process_harvest { + let entry = grouped_hashmap.entry(process.name.clone()).or_insert(( + process.pid, + 0.0, + 0.0, + Vec::new(), + )); + + (*entry).1 += process.cpu_usage_percent; + (*entry).2 += process.mem_usage_percent; + (*entry).3.push(process.pid); + + single_list.insert(process.pid, process.clone()); + } + + let grouped_list: Vec = grouped_hashmap + .iter() + .map(|(name, process_details)| { + let p = process_details.clone(); + ConvertedProcessData { + pid: p.0, + name: name.to_string(), + cpu_usage: p.1, + mem_usage: p.2, + group_pids: p.3, + } + }) + .collect::>(); + + (single_list, grouped_list) +} diff --git a/src/main.rs b/src/main.rs index dde51164..9d739ea0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,11 +35,9 @@ mod canvas; mod constants; mod data_conversion; -use app::data_harvester; -use app::data_harvester::processes::ProcessHarvest; +use app::data_harvester::{self, processes::ProcessSorting}; use constants::TICK_RATE_IN_MILLISECONDS; use data_conversion::*; -use std::collections::BTreeMap; use utils::error::{self, BottomError}; enum Event { @@ -267,7 +265,7 @@ fn main() -> error::Result<()> { } if app.update_process_gui { - handle_process_sorting(&mut app); + update_final_process_list(&mut app); app.update_process_gui = false; } } @@ -279,7 +277,6 @@ fn main() -> error::Result<()> { Event::Update(data) => { if !app.is_frozen { app.data_collection.eat_data(&data); - app.data = *data; // TODO: [OPT] remove this // Convert all data into tui-compliant components @@ -309,7 +306,10 @@ fn main() -> error::Result<()> { update_cpu_data_points(app.show_average_cpu, &app.data_collection); // Processes - handle_process_sorting(&mut app); + let (single, grouped) = convert_process_data(&app.data_collection); + app.canvas_data.process_data = single; + app.canvas_data.grouped_process_data = grouped; + update_final_process_list(&mut app); } } Event::Clean => { @@ -339,82 +339,6 @@ fn main() -> error::Result<()> { Ok(()) } -type TempProcess = (f64, f64, Vec); - -fn handle_process_sorting(app: &mut app::App) { - // Handle combining multi-pid processes to form one entry in table. - // This was done this way to save time and avoid code - // duplication... sorry future me. Really. - - // First, convert this all into a BTreeMap. The key is by name. This - // pulls double duty by allowing us to combine entries AND it sorts! - - // Fields for tuple: CPU%, MEM%, MEM_KB, PID_VEC - let mut process_map: BTreeMap = BTreeMap::new(); - for process in &app.data.list_of_processes { - let entry_val = process_map - .entry(process.name.clone()) - .or_insert((0.0, 0.0, vec![])); - entry_val.0 += process.cpu_usage_percent; - entry_val.1 += process.mem_usage_percent; - entry_val.2.push(process.pid); - } - - // Now... turn this back into the exact same vector... but now with merged processes! - app.data.grouped_list_of_processes = Some( - process_map - .iter() - .map(|(name, data)| { - ProcessHarvest { - pid: 0, // Irrelevant - cpu_usage_percent: data.0, - mem_usage_percent: data.1, - name: name.clone(), - pid_vec: Some(data.2.clone()), - } - }) - .collect::>(), - ); - - if let Some(grouped_list_of_processes) = &mut app.data.grouped_list_of_processes { - if let data_harvester::processes::ProcessSorting::PID = &app.process_sorting_type { - data_harvester::processes::sort_processes( - grouped_list_of_processes, - &data_harvester::processes::ProcessSorting::CPU, // Go back to default, negate PID for group - true, - ); - } else { - data_harvester::processes::sort_processes( - grouped_list_of_processes, - &app.process_sorting_type, - app.process_sorting_reverse, - ); - } - } - - data_harvester::processes::sort_processes( - &mut app.data.list_of_processes, - &app.process_sorting_type, - app.process_sorting_reverse, - ); - - let tuple_results = if app.use_simple { - simple_update_process_row( - &app.data, - &(app.get_current_search_query().to_ascii_lowercase()), - app.is_searching_with_pid(), - ) - } else { - regex_update_process_row( - &app.data, - app.get_current_regex_matcher(), - app.is_searching_with_pid(), - ) - }; - app.canvas_data.process_data = tuple_results.0; - app.canvas_data.grouped_process_data = tuple_results.1; -} - fn cleanup( terminal: &mut tui::terminal::Terminal>, ) -> error::Result<()> { @@ -425,3 +349,73 @@ fn cleanup( Ok(()) } + +fn update_final_process_list(app: &mut app::App) { + let mut filtered_process_data: Vec = if app.is_grouped() { + app.canvas_data + .grouped_process_data + .clone() + .into_iter() + .filter(|process| { + if let Ok(matcher) = app.get_current_regex_matcher() { + matcher.is_match(&process.name) + } else { + true + } + }) + .collect::>() + } else { + app.canvas_data + .process_data + .iter() + .filter(|(_pid, process)| { + if let Ok(matcher) = app.get_current_regex_matcher() { + if app.is_searching_with_pid() { + matcher.is_match(&process.pid.to_string()) + } else { + matcher.is_match(&process.name) + } + } else { + true + } + }) + .map(|(_pid, process)| ConvertedProcessData { + pid: process.pid, + name: process.name.clone(), + cpu_usage: process.cpu_usage_percent, + mem_usage: process.mem_usage_percent, + group_pids: vec![process.pid], + }) + .collect::>() + }; + + sort_process_data(&mut filtered_process_data, app); + app.canvas_data.finalized_process_data = filtered_process_data; +} + +fn sort_process_data(to_sort_vec: &mut Vec, app: &app::App) { + to_sort_vec.sort_by(|a, b| utils::gen_util::get_ordering(&a.name, &b.name, false)); + + match app.process_sorting_type { + ProcessSorting::CPU => { + to_sort_vec.sort_by(|a, b| { + utils::gen_util::get_ordering(a.cpu_usage, b.cpu_usage, app.process_sorting_reverse) + }); + } + ProcessSorting::MEM => { + to_sort_vec.sort_by(|a, b| { + utils::gen_util::get_ordering(a.mem_usage, b.mem_usage, app.process_sorting_reverse) + }); + } + ProcessSorting::NAME => to_sort_vec.sort_by(|a, b| { + utils::gen_util::get_ordering(&a.name, &b.name, app.process_sorting_reverse) + }), + ProcessSorting::PID => { + if !app.is_grouped() { + to_sort_vec.sort_by(|a, b| { + utils::gen_util::get_ordering(a.pid, b.pid, app.process_sorting_reverse) + }); + } + } + } +} diff --git a/src/utils/gen_util.rs b/src/utils/gen_util.rs index 73c04371..04417dab 100644 --- a/src/utils/gen_util.rs +++ b/src/utils/gen_util.rs @@ -59,3 +59,29 @@ pub fn get_simple_byte_values(bytes: u64, spacing: bool) -> (f64, String) { _ => (bytes as f64 / 1_000_000_000_000.0, "TB".to_string()), } } + +/// Gotta get partial ordering? No problem, here's something to deal with it~ +pub fn get_ordering( + a_val: T, b_val: T, reverse_order: bool, +) -> std::cmp::Ordering { + match a_val.partial_cmp(&b_val) { + Some(x) => match x { + Ordering::Greater => { + if reverse_order { + std::cmp::Ordering::Less + } else { + std::cmp::Ordering::Greater + } + } + Ordering::Less => { + if reverse_order { + std::cmp::Ordering::Greater + } else { + std::cmp::Ordering::Less + } + } + Ordering::Equal => Ordering::Equal, + }, + None => Ordering::Equal, + } +}