mirror of
https://github.com/ClementTsang/bottom
synced 2024-11-26 06:00:21 +00:00
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:
parent
d2129056e3
commit
30bdaa6073
14 changed files with 330 additions and 207 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -8,12 +8,15 @@
|
|||
"Nonexhaustive",
|
||||
"Qudsi",
|
||||
"Tebibytes",
|
||||
"Ungrouped",
|
||||
"Wojnarowski",
|
||||
"andys",
|
||||
"crossterm",
|
||||
"curr",
|
||||
"czvf",
|
||||
"gotop",
|
||||
"gtop",
|
||||
"haase",
|
||||
"heim",
|
||||
"hjkl",
|
||||
"markdownlint",
|
||||
|
|
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -207,7 +207,7 @@ 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 |
|
||||
|
@ -215,6 +215,7 @@ Run using `btm`.
|
|||
| `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
|
||||
|
|
104
src/app.rs
104
src/app.rs
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
))
|
||||
},
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
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]
|
||||
};
|
||||
|
|
|
@ -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] = [
|
||||
|
|
|
@ -359,31 +359,25 @@ 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();
|
||||
|
||||
// cpu, mem, pids
|
||||
let mut grouped_hashmap: HashMap<String, SingleProcessData> = 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(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;
|
||||
|
||||
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);
|
||||
|
@ -391,15 +385,19 @@ pub fn convert_process_data(
|
|||
|
||||
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_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 {
|
||||
ConvertedProcessData {
|
||||
pid: process.pid,
|
||||
name: process.name.to_string(),
|
||||
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],
|
||||
|
@ -412,12 +410,36 @@ pub fn convert_process_data(
|
|||
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();
|
||||
|
||||
let grouped_list: Vec<ConvertedProcessData> = grouped_hashmap
|
||||
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;
|
||||
});
|
||||
|
||||
grouped_hashmap
|
||||
.iter()
|
||||
.map(|(name, process_details)| {
|
||||
.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);
|
||||
|
@ -426,7 +448,8 @@ pub fn convert_process_data(
|
|||
|
||||
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_read =
|
||||
format!("{:.*}{}", 0, converted_total_read.0, converted_total_read.1);
|
||||
let total_write = format!(
|
||||
"{:.*}{}",
|
||||
0, converted_total_write.0, converted_total_write.1
|
||||
|
@ -434,7 +457,7 @@ pub fn convert_process_data(
|
|||
|
||||
ConvertedProcessData {
|
||||
pid: p.pid,
|
||||
name: name.to_string(),
|
||||
name: identifier.to_string(),
|
||||
cpu_usage: p.cpu_usage,
|
||||
mem_usage: p.mem_usage,
|
||||
group_pids: p.group_pids,
|
||||
|
@ -449,9 +472,9 @@ pub fn convert_process_data(
|
|||
process_states: p.process_state,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
(single_list, grouped_list)
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert_battery_harvest(
|
||||
|
|
46
src/main.rs
46
src/main.rs
|
@ -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,25 +579,27 @@ 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)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
app.canvas_data
|
||||
let filtered_process_data: Vec<ConvertedProcessData> = app
|
||||
.canvas_data
|
||||
.process_data
|
||||
.iter()
|
||||
.filter(|process| {
|
||||
|
@ -615,11 +614,10 @@ fn update_final_process_list(app: &mut App, widget_id: u64) {
|
|||
}
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
.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| {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue