diff options
author | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2019-10-25 23:23:33 +0300 |
---|---|---|
committer | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2019-10-25 23:23:33 +0300 |
commit | 0a8a8d24622d58df682bad65d6c727df4464d89a (patch) | |
tree | c0d94b307732a82ffeadb87b40e947f4d31b6519 | |
parent | 3b5ca78b01c6fa2103f267a8174eb835fa7d84b2 (diff) |
Add Follow mode for processes
Given a pid, follow the process and its direct children.
-rw-r--r-- | README | 8 | ||||
-rw-r--r-- | src/ui/components/processes.rs | 141 |
2 files changed, 133 insertions, 16 deletions
@@ -15,9 +15,11 @@ BUILD ./target/release/bb SHORTCUTS - 'k' kill process under cursor - press enter to confirm kill, or esc to cancel - 'f' (un)freeze process list updates + 'F' Follow process and its children. Type the pid you want to follow + 'k' kill process under cursor + press enter to confirm kill, or esc to cancel + 'f' (un)freeze process list updates + Esc cancel action AUTHORS Copyright 2019 Manos Pitsidianakis <epilys@nessuent.xyz> Released under the GPL, version 3 or greater. This software carries no warranty of diff --git a/src/ui/components/processes.rs b/src/ui/components/processes.rs index 631240f..2921e51 100644 --- a/src/ui/components/processes.rs +++ b/src/ui/components/processes.rs @@ -147,6 +147,7 @@ pub struct ProcessList { cursor: usize, height: usize, dirty: bool, + force_redraw: bool, maxima: ColumnWidthMaxima, /* stop updating data */ freeze: bool, @@ -158,6 +159,7 @@ pub struct ProcessList { #[derive(Debug, PartialEq)] enum ProcessListMode { Normal, + Follow(Pid), Kill(u16), } @@ -250,6 +252,14 @@ impl ProcessList { freeze: false, mode: Normal, dirty: true, + force_redraw: false, + } + } + + fn follow(&self) -> Option<Pid> { + match self.mode { + ProcessListMode::Follow(pid) => Some(pid), + _ => None, } } } @@ -267,11 +277,32 @@ impl Component for ProcessList { return; } + if self.force_redraw { + self.force_redraw = false; + tick = true; + } + if !self.dirty && !tick { + if let Follow(ref pid) = self.mode { + let (_, y) = write_string_to_grid( + &format!("Following PID == {pid} || PPID == {pid}", pid = pid), + grid, + Color::Default, + Color::Default, + Attr::Bold, + (pos_inc(upper_left!(area), (0, 1)), bottom_right!(area)), + false, + ); + dirty_areas.push_back(( + pos_inc(upper_left!(area), (0, 1)), + set_y(bottom_right!(area), y), + )); + } + return; } - let upper_left = pos_inc(upper_left!(area), (1, 0)); + let mut upper_left = pos_inc(upper_left!(area), (1, 0)); let bottom_right = pos_dec(bottom_right!(area), (1, 1)); /* Reserve first row for column headers */ @@ -304,6 +335,21 @@ impl Component for ProcessList { } if !self.dirty && (!tick/* implies freeze */) && old_cursor != self.cursor { + if let Follow(ref pid) = self.mode { + let (_, y) = write_string_to_grid( + &format!("Following PID == {pid} || PPID == {pid}", pid = pid), + grid, + Color::Default, + Color::Default, + Attr::Bold, + (pos_inc(upper_left!(area), (0, 1)), bottom_right!(area)), + false, + ); + dirty_areas.push_back(( + pos_inc(upper_left!(area), (0, 1)), + set_y(bottom_right!(area), y), + )); + } /* Nothing to update */ return; } @@ -316,7 +362,8 @@ impl Component for ProcessList { let update_maxima = tick && !self.freeze; if update_maxima { - self.processes = get(&mut self.data); + let follow = self.follow(); + self.processes = get(&mut self.data, follow); }; if tick || self.freeze { @@ -341,6 +388,20 @@ impl Component for ProcessList { self.cursor = std::cmp::min(self.height.saturating_sub(1), self.cursor); } + if let Follow(ref pid) = self.mode { + write_string_to_grid( + &format!("Following PID == {pid} || PPID == {pid}", pid = pid), + grid, + Color::Default, + Color::Default, + Attr::Bold, + (pos_inc(upper_left, (0, 1)), bottom_right), + false, + ); + + upper_left = pos_inc(upper_left, (0, 2)); + } + /* Write column headers */ let (x, y) = write_string_to_grid( &format!( @@ -544,6 +605,21 @@ impl Component for ProcessList { y_offset += 1; } } else if old_cursor != self.cursor { + if let Follow(ref pid) = self.mode { + let (_, y) = write_string_to_grid( + &format!("Following PID == {pid} || PPID == {pid}", pid = pid), + grid, + Color::Default, + Color::Default, + Attr::Bold, + (pos_inc(upper_left, (0, 1)), bottom_right), + false, + ); + dirty_areas.push_back((pos_inc(upper_left, (0, 1)), set_y(bottom_right!(area), y))); + + upper_left = pos_inc(upper_left, (0, 2)); + } + let new_area = ( pos_inc(upper_left, (0, self.cursor + 2 - pages * height)), set_y( @@ -562,6 +638,17 @@ impl Component for ProcessList { change_colors(grid, old_area, None, Some(Color::Default)); dirty_areas.push_back(old_area); dirty_areas.push_back(new_area); + } else if let Follow(ref pid) = self.mode { + let (_, y) = write_string_to_grid( + &format!("Following PID == {pid} || PPID == {pid}", pid = pid), + grid, + Color::Default, + Color::Default, + Attr::Bold, + (pos_inc(upper_left, (0, 1)), bottom_right), + false, + ); + dirty_areas.push_back((pos_inc(upper_left, (0, 1)), set_y(bottom_right, y))); } if let Kill(ref n) = self.mode { @@ -722,6 +809,11 @@ impl Component for ProcessList { self.page_movement = Some(PageMovement::End); self.dirty = true; } + UIEvent::Input(k) if *k == map["follow process group"] => { + self.mode = Follow(0); + self.force_redraw = true; + self.dirty = true; + } UIEvent::Input(k) if *k == map["freeze updates"] && self.mode == Normal => { self.freeze = !self.freeze; self.dirty = true; @@ -732,36 +824,51 @@ impl Component for ProcessList { self.dirty = true; } UIEvent::Input(k) if *k == map["cancel"] => { - if let Kill(_) = self.mode { - self.mode = Normal; - self.freeze = false; - self.dirty = true; - } + self.mode = Normal; + self.freeze = false; + self.force_redraw = true; + self.dirty = true; } UIEvent::Input(Key::Char(f)) if self.mode != Normal && f.is_numeric() => { if let Kill(ref mut n) = self.mode { - *n = *n * 10 + (f.to_digit(10).unwrap() as u16); + if let Some(add) = (*n).checked_mul(10) { + *n = add + .checked_add(f.to_digit(10).unwrap() as u16) + .unwrap_or(*n); + } + } else if let Follow(ref mut p) = self.mode { + if let Some(add) = (*p).checked_mul(10) { + *p = add + .checked_add(f.to_digit(10).unwrap() as i32) + .unwrap_or(*p); + } + self.dirty = true; } } UIEvent::Input(Key::Backspace) if self.mode != Normal => { if let Kill(ref mut n) = self.mode { *n = *n / 10; + } else if let Follow(ref mut p) = self.mode { + *p = *p / 10; + self.dirty = true; + self.force_redraw = true; } } UIEvent::Input(Key::Char('\n')) if self.mode != Normal => { - let mut processes = self.processes.iter().collect::<Vec<&ProcessDisplay>>(); - processes.sort_unstable_by(|a, b| b.cpu_percent.cmp(&a.cpu_percent)); if let Kill(ref mut n) = self.mode { use nix::sys::signal::kill; + + let mut processes = self.processes.iter().collect::<Vec<&ProcessDisplay>>(); + processes.sort_unstable_by(|a, b| b.cpu_percent.cmp(&a.cpu_percent)); kill( nix::unistd::Pid::from_raw(processes[self.cursor].i), nix::sys::signal::Signal::from_c_int(*n as i32).unwrap(), ) .ok() .take(); + self.mode = Normal; + self.dirty = true; } - self.mode = Normal; - self.dirty = true; } _ => {} } @@ -774,6 +881,7 @@ impl Component for ProcessList { fn set_dirty(&mut self) {} fn get_shortcuts(&self) -> ShortcutMaps { let mut map: ShortcutMap = Default::default(); + map.insert("follow process group", Key::Char('F')); map.insert("freeze updates", Key::Char('f')); map.insert("kill process", Key::Char('k')); map.insert("cancel", Key::Esc); @@ -819,7 +927,7 @@ fn executable_path_color(p: &CmdLineString) -> Result<(&str, &str, &str), (&str, } } -fn get(data: &mut ProcessData) -> Vec<ProcessDisplay> { +fn get(data: &mut ProcessData, follow_pid: Option<Pid>) -> Vec<ProcessDisplay> { let mut processes = Vec::with_capacity(2048); let cpu_stat = get_stat(&mut 0).remove(0); for entry in std::fs::read_dir("/proc/").unwrap() { @@ -843,6 +951,13 @@ fn get(data: &mut ProcessData) -> Vec<ProcessDisplay> { continue; } + if follow_pid.is_some() + && process.ppid != follow_pid.unwrap() + && process.pid != follow_pid.unwrap() + { + continue; + } + let mut process_display = ProcessDisplay { i: process.pid, pid: PidString(process.pid.to_string()), |