feature: add full command to process widget

This PR adds the ability to toggle between the process name and process path. Currently, this uses `P` as the modifier key.

Currently, the longer command names are dealt with by forcefully changing the width of the columns, but this can be handled in a more graceful manner IMO.
This commit is contained in:
Clement Tsang 2020-08-07 01:29:20 -07:00 committed by GitHub
parent d2129056e3
commit 30bdaa6073
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 330 additions and 207 deletions

View file

@ -8,12 +8,15 @@
"Nonexhaustive",
"Qudsi",
"Tebibytes",
"Ungrouped",
"Wojnarowski",
"andys",
"crossterm",
"curr",
"czvf",
"gotop",
"gtop",
"haase",
"heim",
"hjkl",
"markdownlint",

4
Cargo.lock generated
View file

@ -1340,9 +1340,9 @@ checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41"
[[package]]
name = "tui"
version = "0.9.4"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c25eac88406f384894aa6db56ac0378c767254ee5829824ce5b0fc8fd24d5778"
checksum = "9533d39bef0ae8f510e8a99d78702e68d1bbf0b98a78ec9740509d287010ae1e"
dependencies = [
"bitflags",
"cassowary",

View file

@ -206,15 +206,16 @@ Run using `btm`.
#### Process bindings
| | |
| ------------- | ---------------------------------------------------------- |
| `dd` | Kill the selected process |
| `c` | Sort by CPU usage, press again to reverse sorting order |
| `m` | Sort by memory usage, press again to reverse sorting order |
| `p` | Sort by PID name, press again to reverse sorting order |
| `n` | Sort by process name, press again to reverse sorting order |
| `Tab` | Group/un-group processes with the same name |
| `Ctrl-f`, `/` | Open process search widget |
| | |
| ------------- | ------------------------------------------------------------- |
| `dd` | Kill the selected process |
| `c` | Sort by CPU usage, press again to reverse sorting order |
| `m` | Sort by memory usage, press again to reverse sorting order |
| `p` | Sort by PID name, press again to reverse sorting order |
| `n` | Sort by process name, press again to reverse sorting order |
| `Tab` | Group/un-group processes with the same name |
| `Ctrl-f`, `/` | Open process search widget |
| `P` | Toggle between showing the full path or just the process name |
#### Process search bindings
@ -551,6 +552,7 @@ Thanks to all contributors ([emoji key](https://allcontributors.org/docs/en/emoj
<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
## Thanks

View file

@ -149,8 +149,7 @@ impl App {
BottomWidgetType::Proc => {
if let Some(current_proc_state) = self
.proc_state
.widget_states
.get_mut(&self.current_widget.widget_id)
.get_mut_widget_state(self.current_widget.widget_id)
{
if current_proc_state.is_search_enabled() {
current_proc_state
@ -164,8 +163,7 @@ impl App {
BottomWidgetType::ProcSearch => {
if let Some(current_proc_state) = self
.proc_state
.widget_states
.get_mut(&(self.current_widget.widget_id - 1))
.get_mut_widget_state(self.current_widget.widget_id - 1)
{
if current_proc_state.is_search_enabled() {
current_proc_state
@ -243,8 +241,7 @@ impl App {
BottomWidgetType::Cpu => {
if let Some(cpu_widget_state) = self
.cpu_state
.widget_states
.get_mut(&self.current_widget.widget_id)
.get_mut_widget_state(self.current_widget.widget_id)
{
cpu_widget_state.is_multi_graph_mode =
!cpu_widget_state.is_multi_graph_mode;
@ -253,8 +250,7 @@ impl App {
BottomWidgetType::Proc => {
if let Some(proc_widget_state) = self
.proc_state
.widget_states
.get_mut(&self.current_widget.widget_id)
.get_mut_widget_state(self.current_widget.widget_id)
{
// Toggles process widget grouping state
proc_widget_state.is_grouped = !(proc_widget_state.is_grouped);
@ -285,8 +281,7 @@ impl App {
// Toggle on
if let Some(proc_widget_state) = self
.proc_state
.widget_states
.get_mut(&self.current_widget.widget_id)
.get_mut_widget_state(self.current_widget.widget_id)
{
proc_widget_state
.process_search_state
@ -500,12 +495,20 @@ impl App {
pub fn on_left_key(&mut self) {
if !self.is_in_dialog() {
match self.current_widget.widget_type {
BottomWidgetType::Proc => {
if let Some(proc_widget_state) = self
.proc_state
.get_mut_widget_state(self.current_widget.widget_id)
{
proc_widget_state.current_column_index =
proc_widget_state.current_column_index.saturating_sub(1);
}
}
BottomWidgetType::ProcSearch => {
let is_in_search_widget = self.is_in_search_widget();
if let Some(proc_widget_state) = self
.proc_state
.widget_states
.get_mut(&(self.current_widget.widget_id - 1))
.get_mut_widget_state(self.current_widget.widget_id - 1)
{
if is_in_search_widget {
let prev_cursor = proc_widget_state.get_cursor_position();
@ -533,8 +536,7 @@ impl App {
if !self.canvas_data.battery_data.is_empty() {
if let Some(battery_widget_state) = self
.battery_state
.widget_states
.get_mut(&self.current_widget.widget_id)
.get_mut_widget_state(self.current_widget.widget_id)
{
if battery_widget_state.currently_selected_battery_index > 0 {
battery_widget_state.currently_selected_battery_index -= 1;
@ -552,12 +554,21 @@ impl App {
pub fn on_right_key(&mut self) {
if !self.is_in_dialog() {
match self.current_widget.widget_type {
BottomWidgetType::Proc => {
if let Some(proc_widget_state) = self
.proc_state
.get_mut_widget_state(self.current_widget.widget_id)
{
if proc_widget_state.current_column_index < proc_widget_state.num_columns {
proc_widget_state.current_column_index += 1;
}
}
}
BottomWidgetType::ProcSearch => {
let is_in_search_widget = self.is_in_search_widget();
if let Some(proc_widget_state) = self
.proc_state
.widget_states
.get_mut(&(self.current_widget.widget_id - 1))
.get_mut_widget_state(self.current_widget.widget_id - 1)
{
if is_in_search_widget {
let prev_cursor = proc_widget_state.get_cursor_position();
@ -586,8 +597,7 @@ impl App {
let battery_count = self.canvas_data.battery_data.len();
if let Some(battery_widget_state) = self
.battery_state
.widget_states
.get_mut(&self.current_widget.widget_id)
.get_mut_widget_state(self.current_widget.widget_id)
{
if battery_widget_state.currently_selected_battery_index
< battery_count - 1
@ -887,8 +897,7 @@ impl App {
if let BottomWidgetType::Proc = self.current_widget.widget_type {
if let Some(proc_widget_state) = self
.proc_state
.widget_states
.get_mut(&self.current_widget.widget_id)
.get_mut_widget_state(self.current_widget.widget_id)
{
match proc_widget_state.process_sorting_type {
processes::ProcessSorting::CPU => {
@ -911,8 +920,7 @@ impl App {
if let BottomWidgetType::Proc = self.current_widget.widget_type {
if let Some(proc_widget_state) = self
.proc_state
.widget_states
.get_mut(&self.current_widget.widget_id)
.get_mut_widget_state(self.current_widget.widget_id)
{
match proc_widget_state.process_sorting_type {
processes::ProcessSorting::MEM => {
@ -934,8 +942,7 @@ impl App {
if let BottomWidgetType::Proc = self.current_widget.widget_type {
if let Some(proc_widget_state) = self
.proc_state
.widget_states
.get_mut(&self.current_widget.widget_id)
.get_mut_widget_state(self.current_widget.widget_id)
{
// Skip if grouped
if !proc_widget_state.is_grouped {
@ -956,21 +963,32 @@ impl App {
}
}
}
'P' => {
if let BottomWidgetType::Proc = self.current_widget.widget_type {
if let Some(proc_widget_state) = self
.proc_state
.get_mut_widget_state(self.current_widget.widget_id)
{
proc_widget_state.is_using_full_path =
!proc_widget_state.is_using_full_path;
self.proc_state.force_update = Some(self.current_widget.widget_id);
}
}
}
'n' => {
if let BottomWidgetType::Proc = self.current_widget.widget_type {
if let Some(proc_widget_state) = self
.proc_state
.widget_states
.get_mut(&self.current_widget.widget_id)
.get_mut_widget_state(self.current_widget.widget_id)
{
match proc_widget_state.process_sorting_type {
processes::ProcessSorting::NAME => {
processes::ProcessSorting::IDENTIFIER => {
proc_widget_state.process_sorting_reverse =
!proc_widget_state.process_sorting_reverse
}
_ => {
proc_widget_state.process_sorting_type =
processes::ProcessSorting::NAME;
processes::ProcessSorting::IDENTIFIER;
proc_widget_state.process_sorting_reverse = false;
}
}
@ -1371,8 +1389,7 @@ impl App {
if let Some(new_widget) = self.widget_map.get(&new_widget_id) {
if let Some(proc_widget_state) = self
.proc_state
.widget_states
.get(&self.current_widget.widget_id)
.get_widget_state(self.current_widget.widget_id)
{
if proc_widget_state.is_search_enabled() {
self.current_widget = new_widget.clone();
@ -1393,8 +1410,7 @@ impl App {
BottomWidgetType::Proc => {
if let Some(proc_widget_state) = self
.proc_state
.widget_states
.get_mut(&self.current_widget.widget_id)
.get_mut_widget_state(self.current_widget.widget_id)
{
proc_widget_state.scroll_state.current_scroll_position = 0;
proc_widget_state.scroll_state.scroll_direction = ScrollDirection::UP;
@ -1403,8 +1419,7 @@ impl App {
BottomWidgetType::Temp => {
if let Some(temp_widget_state) = self
.temp_state
.widget_states
.get_mut(&self.current_widget.widget_id)
.get_mut_widget_state(self.current_widget.widget_id)
{
temp_widget_state.scroll_state.current_scroll_position = 0;
temp_widget_state.scroll_state.scroll_direction = ScrollDirection::UP;
@ -1413,8 +1428,7 @@ impl App {
BottomWidgetType::Disk => {
if let Some(disk_widget_state) = self
.disk_state
.widget_states
.get_mut(&self.current_widget.widget_id)
.get_mut_widget_state(self.current_widget.widget_id)
{
disk_widget_state.scroll_state.current_scroll_position = 0;
disk_widget_state.scroll_state.scroll_direction = ScrollDirection::UP;
@ -1423,8 +1437,7 @@ impl App {
BottomWidgetType::CpuLegend => {
if let Some(cpu_widget_state) = self
.cpu_state
.widget_states
.get_mut(&(self.current_widget.widget_id - 1))
.get_mut_widget_state(self.current_widget.widget_id - 1)
{
cpu_widget_state.scroll_state.current_scroll_position = 0;
cpu_widget_state.scroll_state.scroll_direction = ScrollDirection::UP;
@ -1445,8 +1458,7 @@ impl App {
BottomWidgetType::Proc => {
if let Some(proc_widget_state) = self
.proc_state
.widget_states
.get_mut(&self.current_widget.widget_id)
.get_mut_widget_state(self.current_widget.widget_id)
{
if let Some(finalized_process_data) = self
.canvas_data
@ -1465,8 +1477,7 @@ impl App {
BottomWidgetType::Temp => {
if let Some(temp_widget_state) = self
.temp_state
.widget_states
.get_mut(&self.current_widget.widget_id)
.get_mut_widget_state(self.current_widget.widget_id)
{
if !self.canvas_data.temp_sensor_data.is_empty() {
temp_widget_state.scroll_state.current_scroll_position =
@ -1478,8 +1489,7 @@ impl App {
BottomWidgetType::Disk => {
if let Some(disk_widget_state) = self
.disk_state
.widget_states
.get_mut(&self.current_widget.widget_id)
.get_mut_widget_state(self.current_widget.widget_id)
{
if !self.canvas_data.disk_data.is_empty() {
disk_widget_state.scroll_state.current_scroll_position =
@ -1491,8 +1501,7 @@ impl App {
BottomWidgetType::CpuLegend => {
if let Some(cpu_widget_state) = self
.cpu_state
.widget_states
.get_mut(&(self.current_widget.widget_id - 1))
.get_mut_widget_state(self.current_widget.widget_id - 1)
{
let cap = self.canvas_data.cpu_data.len();
if cap > 0 {
@ -1564,8 +1573,7 @@ impl App {
fn change_process_position(&mut self, num_to_change_by: i64) {
if let Some(proc_widget_state) = self
.proc_state
.widget_states
.get_mut(&self.current_widget.widget_id)
.get_mut_widget_state(self.current_widget.widget_id)
{
let current_posn = proc_widget_state.scroll_state.current_scroll_position;

View file

@ -17,7 +17,7 @@ pub enum ProcessSorting {
CPU,
MEM,
PID,
NAME,
IDENTIFIER,
}
impl Default for ProcessSorting {
@ -32,6 +32,7 @@ pub struct ProcessHarvest {
pub cpu_usage_percent: f64,
pub mem_usage_percent: f64,
pub name: String,
pub path: String,
pub read_bytes_per_sec: u64,
pub write_bytes_per_sec: u64,
pub total_read_bytes: u64,
@ -46,6 +47,7 @@ pub struct PrevProcDetails {
pub total_write_bytes: u64,
pub cpu_time: f64,
pub proc_stat_path: PathBuf,
pub proc_exe_path: PathBuf,
pub proc_io_path: PathBuf,
}
@ -54,6 +56,7 @@ impl PrevProcDetails {
let pid_string = pid.to_string();
PrevProcDetails {
proc_io_path: PathBuf::from(format!("/proc/{}/io", pid_string)),
proc_exe_path: PathBuf::from(format!("/proc/{}/exe", pid_string)),
proc_stat_path: PathBuf::from(format!("/proc/{}/stat", pid_string)),
..PrevProcDetails::default()
}
@ -174,7 +177,9 @@ fn get_linux_cpu_usage(
// Based heavily on https://stackoverflow.com/a/23376195 and https://stackoverflow.com/a/1424556
let after_proc_val = get_process_cpu_stats(&proc_stats);
if use_current_cpu_total {
if cpu_usage == 0.0 {
Ok((0_f64, after_proc_val))
} else if use_current_cpu_total {
Ok((
(after_proc_val - before_proc_val) / cpu_usage * 100_f64,
after_proc_val,
@ -194,17 +199,18 @@ fn convert_ps<S: core::hash::BuildHasher>(
new_pid_stats: &mut HashMap<u32, PrevProcDetails, S>, use_current_cpu_total: bool,
time_difference_in_secs: u64,
) -> std::io::Result<ProcessHarvest> {
let pid = (&process[..11])
let pid = (&process[..10])
.trim()
.to_string()
.parse::<u32>()
.unwrap_or(0);
let name = (&process[11..61]).trim().to_string();
let mem_usage_percent = (&process[62..])
let name = (&process[11..111]).trim().to_string();
let mem_usage_percent = (&process[112..116])
.trim()
.to_string()
.parse::<f64>()
.unwrap_or(0_f64);
let path = (&process[117..]).trim().to_string();
let mut new_pid_stat = if let Some(prev_proc_stats) = prev_pid_stats.remove(&pid) {
prev_proc_stats
@ -265,9 +271,11 @@ fn convert_ps<S: core::hash::BuildHasher>(
new_pid_stats.insert(pid, new_pid_stat);
// TODO: Is there a way to re-use these stats so I don't have to do so many syscalls?
Ok(ProcessHarvest {
pid,
name,
path,
mem_usage_percent,
cpu_usage_percent,
total_read_bytes,
@ -286,7 +294,7 @@ pub fn linux_get_processes_list(
time_difference_in_secs: u64,
) -> crate::utils::error::Result<Vec<ProcessHarvest>> {
let ps_result = Command::new("ps")
.args(&["-axo", "pid:10,comm:50,%mem:5", "--noheader"])
.args(&["-axo", "pid:10,comm:100,%mem:5,args:100", "--noheader"])
.output()?;
let ps_stdout = String::from_utf8_lossy(&ps_result.stdout);
let split_string = ps_stdout.split('\n');
@ -356,13 +364,21 @@ pub fn windows_macos_get_processes_list(
} else {
process_val.name().to_string()
};
let path = {
let path = process_val.cmd().join(" ");
if path.is_empty() {
name.to_string()
} else {
path
}
};
let pcu = if cfg!(target_os = "windows") {
let pcu = if cfg!(target_os = "windows") || num_cpus == 0.0 {
process_val.cpu_usage() as f64
} else {
process_val.cpu_usage() as f64 / num_cpus
};
let process_cpu_usage = if use_current_cpu_total {
let process_cpu_usage = if use_current_cpu_total && cpu_usage > 0.0 {
pcu / cpu_usage
} else {
pcu
@ -373,6 +389,7 @@ pub fn windows_macos_get_processes_list(
process_vector.push(ProcessHarvest {
pid: process_val.pid() as u32,
name,
path,
mem_usage_percent: if mem_total_kb > 0 {
process_val.memory() as f64 * 100.0 / mem_total_kb as f64
} else {

View file

@ -147,6 +147,9 @@ pub struct ProcWidgetState {
pub scroll_state: AppScrollWidgetState,
pub process_sorting_type: processes::ProcessSorting,
pub process_sorting_reverse: bool,
pub is_using_full_path: bool,
pub current_column_index: usize,
pub num_columns: usize,
}
impl ProcWidgetState {
@ -171,6 +174,9 @@ impl ProcWidgetState {
scroll_state: AppScrollWidgetState::default(),
process_sorting_type: processes::ProcessSorting::CPU,
process_sorting_reverse: true,
is_using_full_path: false,
current_column_index: 0,
num_columns: 1,
}
}
@ -263,6 +269,14 @@ impl ProcState {
force_update_all: false,
}
}
pub fn get_mut_widget_state(&mut self, widget_id: u64) -> Option<&mut ProcWidgetState> {
self.widget_states.get_mut(&widget_id)
}
pub fn get_widget_state(&self, widget_id: u64) -> Option<&ProcWidgetState> {
self.widget_states.get(&widget_id)
}
}
pub struct NetWidgetState {
@ -291,6 +305,14 @@ impl NetState {
widget_states,
}
}
pub fn get_mut_widget_state(&mut self, widget_id: u64) -> Option<&mut NetWidgetState> {
self.widget_states.get_mut(&widget_id)
}
pub fn get_widget_state(&self, widget_id: u64) -> Option<&NetWidgetState> {
self.widget_states.get(&widget_id)
}
}
pub struct CpuWidgetState {
@ -325,6 +347,14 @@ impl CpuState {
widget_states,
}
}
pub fn get_mut_widget_state(&mut self, widget_id: u64) -> Option<&mut CpuWidgetState> {
self.widget_states.get_mut(&widget_id)
}
pub fn get_widget_state(&self, widget_id: u64) -> Option<&CpuWidgetState> {
self.widget_states.get(&widget_id)
}
}
pub struct MemWidgetState {
@ -353,6 +383,14 @@ impl MemState {
widget_states,
}
}
pub fn get_mut_widget_state(&mut self, widget_id: u64) -> Option<&mut MemWidgetState> {
self.widget_states.get_mut(&widget_id)
}
pub fn get_widget_state(&self, widget_id: u64) -> Option<&MemWidgetState> {
self.widget_states.get(&widget_id)
}
}
pub struct TempWidgetState {
@ -375,6 +413,14 @@ impl TempState {
pub fn init(widget_states: HashMap<u64, TempWidgetState>) -> Self {
TempState { widget_states }
}
pub fn get_mut_widget_state(&mut self, widget_id: u64) -> Option<&mut TempWidgetState> {
self.widget_states.get_mut(&widget_id)
}
pub fn get_widget_state(&self, widget_id: u64) -> Option<&TempWidgetState> {
self.widget_states.get(&widget_id)
}
}
pub struct DiskWidgetState {
@ -397,6 +443,14 @@ impl DiskState {
pub fn init(widget_states: HashMap<u64, DiskWidgetState>) -> Self {
DiskState { widget_states }
}
pub fn get_mut_widget_state(&mut self, widget_id: u64) -> Option<&mut DiskWidgetState> {
self.widget_states.get_mut(&widget_id)
}
pub fn get_widget_state(&self, widget_id: u64) -> Option<&DiskWidgetState> {
self.widget_states.get(&widget_id)
}
}
pub struct BasicTableWidgetState {
// Since this is intended (currently) to only be used for ONE widget, that's
@ -420,6 +474,14 @@ impl BatteryState {
pub fn init(widget_states: HashMap<u64, BatteryWidgetState>) -> Self {
BatteryState { widget_states }
}
pub fn get_mut_widget_state(&mut self, widget_id: u64) -> Option<&mut BatteryWidgetState> {
self.widget_states.get_mut(&widget_id)
}
pub fn get_widget_state(&self, widget_id: u64) -> Option<&BatteryWidgetState> {
self.widget_states.get(&widget_id)
}
}
#[derive(Default)]

View file

@ -40,8 +40,6 @@ pub struct DisplayableData {
pub temp_sensor_data: Vec<Vec<String>>,
// Not the final value
pub process_data: Vec<ConvertedProcessData>,
// Not the final value
pub grouped_process_data: Vec<ConvertedProcessData>,
// What's actually displayed
pub finalized_process_data_map: HashMap<u64, Vec<ConvertedProcessData>>,
pub mem_label: String,

View file

@ -28,20 +28,19 @@ impl KillDialog for Painter {
if app_state.is_grouped(app_state.current_widget.widget_id) {
if to_kill_processes.1.len() != 1 {
Text::raw(format!(
"\nKill {} processes with the name {}?",
"\nKill {} processes with the name \"{}\"?",
to_kill_processes.1.len(),
to_kill_processes.0
))
} else {
Text::raw(format!(
"\nKill {} process with the name {}?",
to_kill_processes.1.len(),
"\nKill 1 process with the name \"{}\"?",
to_kill_processes.0
))
}
} else {
Text::raw(format!(
"\nKill process {} with PID {}?",
"\nKill process \"{}\" with PID {}?",
to_kill_processes.0, first_pid
))
},

View file

@ -31,7 +31,7 @@ impl HelpDialog for Painter {
// small terminal sizes... oh joy.
let mut overflow_buffer = 0;
let paragraph_width = draw_loc.width - 2;
let paragraph_width = std::cmp::max(draw_loc.width.saturating_sub(2), 1);
let mut prev_section_len = 0;
constants::HELP_TEXT

View file

@ -133,13 +133,17 @@ impl ProcessTableWidget for Painter {
});
use app::data_harvester::processes::ProcessSorting;
let mut pid_or_name = if proc_widget_state.is_grouped {
let mut pid_or_count = if proc_widget_state.is_grouped {
"Count"
} else {
"PID(p)"
}
.to_string();
let mut name = "Name(n)".to_string();
let mut identifier = if proc_widget_state.is_using_full_path {
"Command(n)".to_string()
} else {
"Name(n)".to_string()
};
let mut cpu = "CPU%(c)".to_string();
let mut mem = "Mem%(m)".to_string();
let rps = "R/s".to_string();
@ -157,14 +161,15 @@ impl ProcessTableWidget for Painter {
match proc_widget_state.process_sorting_type {
ProcessSorting::CPU => cpu += &direction_val,
ProcessSorting::MEM => mem += &direction_val,
ProcessSorting::PID => pid_or_name += &direction_val,
ProcessSorting::NAME => name += &direction_val,
ProcessSorting::PID => pid_or_count += &direction_val,
ProcessSorting::IDENTIFIER => identifier += &direction_val,
};
// TODO: Gonna have to figure out how to do left/right GUI notation.
let process_headers = if proc_widget_state.is_grouped {
vec![
pid_or_name,
name,
pid_or_count,
identifier,
cpu,
mem,
rps,
@ -174,8 +179,8 @@ impl ProcessTableWidget for Painter {
]
} else {
vec![
pid_or_name,
name,
pid_or_count,
identifier,
cpu,
mem,
rps,
@ -185,6 +190,7 @@ impl ProcessTableWidget for Painter {
process_state,
]
};
proc_widget_state.num_columns = process_headers.len();
let process_headers_lens: Vec<usize> = process_headers
.iter()
.map(|entry| entry.len())
@ -192,8 +198,16 @@ impl ProcessTableWidget for Painter {
// Calculate widths
let width = f64::from(draw_loc.width);
// TODO: This is a ugly work-around for now.
let width_ratios = if proc_widget_state.is_grouped {
vec![0.1, 0.2, 0.1, 0.1, 0.1, 0.1, 0.15, 0.15]
if proc_widget_state.is_using_full_path {
vec![0.1, 0.7, 0.05, 0.05, 0.025, 0.025, 0.025, 0.025]
} else {
vec![0.1, 0.2, 0.1, 0.1, 0.1, 0.1, 0.15, 0.15]
}
} else if proc_widget_state.is_using_full_path {
vec![0.1, 0.7, 0.05, 0.05, 0.02, 0.02, 0.02, 0.02, 0.02]
} else {
vec![0.1, 0.2, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]
};

View file

@ -79,7 +79,7 @@ pub const CPU_HELP_TEXT: [&str; 2] = [
"Mouse scroll Scrolling over an CPU core/average shows only that entry on the chart",
];
pub const PROCESS_HELP_TEXT: [&str; 8] = [
pub const PROCESS_HELP_TEXT: [&str; 9] = [
"3 - Process widget\n",
"dd Kill the selected process\n",
"c Sort by CPU usage, press again to reverse sorting order\n",
@ -87,7 +87,8 @@ pub const PROCESS_HELP_TEXT: [&str; 8] = [
"p Sort by PID name, press again to reverse sorting order\n",
"n Sort by process name, press again to reverse sorting order\n",
"Tab Group/un-group processes with the same name\n",
"Ctrl-f, / Open process search widget",
"Ctrl-f, / Open process search widget\n",
"P Toggle between showing the full path or just the process name",
];
pub const SEARCH_HELP_TEXT: [&str; 43] = [

View file

@ -359,99 +359,122 @@ pub fn convert_network_data_points(
}
}
pub enum ProcessGroupingType {
Grouped,
Ungrouped,
}
pub enum ProcessNamingType {
Name,
Path,
}
pub fn convert_process_data(
current_data: &data_farmer::DataCollection,
) -> (Vec<ConvertedProcessData>, Vec<ConvertedProcessData>) {
let mut single_list = Vec::new();
current_data: &data_farmer::DataCollection, grouping_type: ProcessGroupingType,
name_type: ProcessNamingType,
) -> Vec<ConvertedProcessData> {
match grouping_type {
ProcessGroupingType::Ungrouped => current_data
.process_harvest
.iter()
.map(|process| {
let converted_rps = get_exact_byte_values(process.read_bytes_per_sec, false);
let converted_wps = get_exact_byte_values(process.write_bytes_per_sec, false);
let converted_total_read = get_exact_byte_values(process.total_read_bytes, false);
let converted_total_write = get_exact_byte_values(process.total_write_bytes, false);
// cpu, mem, pids
let mut grouped_hashmap: HashMap<String, SingleProcessData> = std::collections::HashMap::new();
let read_per_sec = format!("{:.*}{}/s", 0, converted_rps.0, converted_rps.1);
let write_per_sec = format!("{:.*}{}/s", 0, converted_wps.0, converted_wps.1);
let total_read =
format!("{:.*}{}", 0, converted_total_read.0, converted_total_read.1);
let total_write = format!(
"{:.*}{}",
0, converted_total_write.0, converted_total_write.1
);
// 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(SingleProcessData {
pid: process.pid,
..SingleProcessData::default()
ConvertedProcessData {
pid: process.pid,
name: match name_type {
ProcessNamingType::Name => process.name.to_string(),
ProcessNamingType::Path => process.path.to_string(),
},
cpu_usage: process.cpu_usage_percent,
mem_usage: process.mem_usage_percent,
group_pids: vec![process.pid],
read_per_sec,
write_per_sec,
total_read,
total_write,
rps_f64: process.read_bytes_per_sec as f64,
wps_f64: process.write_bytes_per_sec as f64,
tr_f64: process.total_read_bytes as f64,
tw_f64: process.total_write_bytes as f64,
process_states: process.process_state.to_owned(),
}
})
.collect::<Vec<_>>(),
ProcessGroupingType::Grouped => {
let mut grouped_hashmap: HashMap<String, SingleProcessData> =
std::collections::HashMap::new();
current_data.process_harvest.iter().for_each(|process| {
let entry = grouped_hashmap
.entry(match name_type {
ProcessNamingType::Name => process.name.to_string(),
ProcessNamingType::Path => process.path.to_string(),
})
.or_insert(SingleProcessData {
pid: process.pid,
..SingleProcessData::default()
});
(*entry).cpu_usage += process.cpu_usage_percent;
(*entry).mem_usage += process.mem_usage_percent;
(*entry).group_pids.push(process.pid);
(*entry).read_per_sec += process.read_bytes_per_sec;
(*entry).write_per_sec += process.write_bytes_per_sec;
(*entry).total_read += process.total_read_bytes;
(*entry).total_write += process.total_write_bytes;
});
(*entry).cpu_usage += process.cpu_usage_percent;
(*entry).mem_usage += process.mem_usage_percent;
(*entry).group_pids.push(process.pid);
(*entry).read_per_sec += process.read_bytes_per_sec;
(*entry).write_per_sec += process.write_bytes_per_sec;
(*entry).total_read += process.total_read_bytes;
(*entry).total_write += process.total_write_bytes;
grouped_hashmap
.iter()
.map(|(identifier, process_details)| {
let p = process_details.clone();
let converted_rps = get_exact_byte_values(p.read_per_sec, false);
let converted_wps = get_exact_byte_values(p.write_per_sec, false);
let converted_total_read = get_exact_byte_values(p.total_read, false);
let converted_total_write = get_exact_byte_values(p.total_write, false);
let converted_rps = get_exact_byte_values(process.read_bytes_per_sec, false);
let converted_wps = get_exact_byte_values(process.write_bytes_per_sec, false);
let converted_total_read = get_exact_byte_values(process.total_read_bytes, false);
let converted_total_write = get_exact_byte_values(process.total_write_bytes, false);
let read_per_sec = format!("{:.*}{}/s", 0, converted_rps.0, converted_rps.1);
let write_per_sec = format!("{:.*}{}/s", 0, converted_wps.0, converted_wps.1);
let total_read =
format!("{:.*}{}", 0, converted_total_read.0, converted_total_read.1);
let total_write = format!(
"{:.*}{}",
0, converted_total_write.0, converted_total_write.1
);
let read_per_sec = format!("{:.*}{}/s", 0, converted_rps.0, converted_rps.1);
let write_per_sec = format!("{:.*}{}/s", 0, converted_wps.0, converted_wps.1);
let total_read = format!("{:.*}{}", 0, converted_total_read.0, converted_total_read.1);
let total_write = format!(
"{:.*}{}",
0, converted_total_write.0, converted_total_write.1
);
single_list.push(ConvertedProcessData {
pid: process.pid,
name: process.name.to_string(),
cpu_usage: process.cpu_usage_percent,
mem_usage: process.mem_usage_percent,
group_pids: vec![process.pid],
read_per_sec,
write_per_sec,
total_read,
total_write,
rps_f64: process.read_bytes_per_sec as f64,
wps_f64: process.write_bytes_per_sec as f64,
tr_f64: process.total_read_bytes as f64,
tw_f64: process.total_write_bytes as f64,
process_states: process.process_state.to_owned(),
});
ConvertedProcessData {
pid: p.pid,
name: identifier.to_string(),
cpu_usage: p.cpu_usage,
mem_usage: p.mem_usage,
group_pids: p.group_pids,
read_per_sec,
write_per_sec,
total_read,
total_write,
rps_f64: p.read_per_sec as f64,
wps_f64: p.write_per_sec as f64,
tr_f64: p.total_read as f64,
tw_f64: p.total_write as f64,
process_states: p.process_state,
}
})
.collect::<Vec<_>>()
}
}
let grouped_list: Vec<ConvertedProcessData> = grouped_hashmap
.iter()
.map(|(name, process_details)| {
let p = process_details.clone();
let converted_rps = get_exact_byte_values(p.read_per_sec, false);
let converted_wps = get_exact_byte_values(p.write_per_sec, false);
let converted_total_read = get_exact_byte_values(p.total_read, false);
let converted_total_write = get_exact_byte_values(p.total_write, false);
let read_per_sec = format!("{:.*}{}/s", 0, converted_rps.0, converted_rps.1);
let write_per_sec = format!("{:.*}{}/s", 0, converted_wps.0, converted_wps.1);
let total_read = format!("{:.*}{}", 0, converted_total_read.0, converted_total_read.1);
let total_write = format!(
"{:.*}{}",
0, converted_total_write.0, converted_total_write.1
);
ConvertedProcessData {
pid: p.pid,
name: name.to_string(),
cpu_usage: p.cpu_usage,
mem_usage: p.mem_usage,
group_pids: p.group_pids,
read_per_sec,
write_per_sec,
total_read,
total_write,
rps_f64: p.read_per_sec as f64,
wps_f64: p.write_per_sec as f64,
tr_f64: p.total_read as f64,
tw_f64: p.total_write as f64,
process_states: p.process_state,
}
})
.collect::<Vec<_>>();
(single_list, grouped_list)
}
pub fn convert_battery_harvest(

View file

@ -225,9 +225,6 @@ fn main() -> error::Result<()> {
// Processes
if app.used_widgets.use_proc {
let (single, grouped) = convert_process_data(&app.data_collection);
app.canvas_data.process_data = single;
app.canvas_data.grouped_process_data = grouped;
update_all_process_lists(&mut app);
}
@ -582,44 +579,45 @@ fn update_final_process_list(app: &mut App, widget_id: u64) {
.is_invalid_or_blank_search(),
None => false,
};
let is_grouped = app.is_grouped(widget_id);
if let Some(proc_widget_state) = app.proc_state.get_mut_widget_state(widget_id) {
app.canvas_data.process_data = convert_process_data(
&app.data_collection,
if is_grouped {
ProcessGroupingType::Grouped
} else {
ProcessGroupingType::Ungrouped
},
if proc_widget_state.is_using_full_path {
ProcessNamingType::Path
} else {
ProcessNamingType::Name
},
);
}
let process_filter = app.get_process_filter(widget_id);
let filtered_process_data: Vec<ConvertedProcessData> = if app.is_grouped(widget_id) {
app.canvas_data
.grouped_process_data
.iter()
.filter(|process| {
if is_invalid_or_blank {
true
} else if let Some(process_filter) = process_filter {
process_filter.check(process)
let filtered_process_data: Vec<ConvertedProcessData> = app
.canvas_data
.process_data
.iter()
.filter(|process| {
if !is_invalid_or_blank {
if let Some(process_filter) = process_filter {
process_filter.check(&process)
} else {
true
}
})
.cloned()
.collect::<Vec<_>>()
} else {
app.canvas_data
.process_data
.iter()
.filter(|process| {
if !is_invalid_or_blank {
if let Some(process_filter) = process_filter {
process_filter.check(&process)
} else {
true
}
} else {
true
}
})
.cloned()
.collect::<Vec<_>>()
};
} else {
true
}
})
.cloned()
.collect::<Vec<_>>();
// Quick fix for tab updating the table headers
if let Some(proc_widget_state) = app.proc_state.widget_states.get_mut(&widget_id) {
if let Some(proc_widget_state) = app.proc_state.get_mut_widget_state(widget_id) {
if let data_harvester::processes::ProcessSorting::PID =
proc_widget_state.process_sorting_type
{
@ -672,7 +670,7 @@ fn sort_process_data(
)
});
}
ProcessSorting::NAME => {
ProcessSorting::IDENTIFIER => {
// Don't repeat if false...
if proc_widget_state.process_sorting_reverse {
to_sort_vec.sort_by(|a, b| {

View file

@ -357,8 +357,6 @@ fn get_temperature(
/// Yes, this function gets whether to show average CPU (true) or not (false)
fn get_show_average_cpu(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
// FIXME: Update the demo config file and default config files! Need to remove
// old options and change to hide_avg_cpu option.
if matches.is_present("HIDE_AVG_CPU") {
return false;
} else if let Some(flags) = &config.flags {