diff options
author | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2019-09-04 17:39:36 +0300 |
---|---|---|
committer | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2019-09-04 20:07:02 +0300 |
commit | f759b54a54c48ea46388f01c1d550e3e24f81afd (patch) | |
tree | ff9982e5323105ad76f209c6a153361d5d739d3e /src | |
parent | e9bab5453d003de26e2b2ba81277afa676eba67e (diff) |
first version
Diffstat (limited to 'src')
-rw-r--r-- | src/main.rs | 106 | ||||
-rw-r--r-- | src/thread.rs | 221 | ||||
-rw-r--r-- | src/ui.rs | 12 | ||||
-rw-r--r-- | src/ui/components.rs | 79 | ||||
-rw-r--r-- | src/ui/components/kernel.rs | 46 | ||||
-rw-r--r-- | src/ui/components/processes.rs | 517 | ||||
-rw-r--r-- | src/ui/components/utilities.rs | 356 | ||||
-rw-r--r-- | src/ui/components/utilities/widgets.rs | 69 | ||||
-rw-r--r-- | src/ui/state.rs | 21 | ||||
-rw-r--r-- | src/ui/terminal.rs | 12 | ||||
-rw-r--r-- | src/ui/terminal/cells.rs | 169 | ||||
-rw-r--r-- | src/ui/terminal/keys.rs | 17 | ||||
-rw-r--r-- | src/ui/terminal/position.rs | 12 | ||||
-rw-r--r-- | src/ui/text_processing.rs | 21 | ||||
-rw-r--r-- | src/ui/text_processing/grapheme_clusters.rs | 21 | ||||
-rw-r--r-- | src/ui/text_processing/line_break.rs | 21 | ||||
-rw-r--r-- | src/ui/text_processing/tables.rs | 21 | ||||
-rw-r--r-- | src/ui/text_processing/types.rs | 21 | ||||
-rw-r--r-- | src/ui/text_processing/wcwidth.rs | 21 | ||||
-rw-r--r-- | src/ui/types.rs | 39 |
20 files changed, 943 insertions, 859 deletions
diff --git a/src/main.rs b/src/main.rs index 5c0be61..94df77a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,10 +34,12 @@ use crossbeam::channel::{bounded, tick}; use crossbeam::select; use libc::c_int; use std::io::Error; -use std::time::{Duration, Instant}; +use std::time::Duration; mod ui; use ui::*; +mod thread; +use thread::start_thread; fn notify(signals: &[c_int]) -> Result<crossbeam::channel::Receiver<c_int>, Error> { let (s, r) = bounded(100); @@ -69,66 +71,62 @@ fn main() -> Result<(), Error> { let receiver = state.receiver(); - let window = Box::new(HSplit::new( + /* Start data update thread (src/thread.rs) */ + let (s, r) = start_thread(); + let window = Box::new(Window::new( Box::new(ui::components::KernelMetrics::new()), - Box::new(ui::components::ProcessList::new()), - 83, - false, + Box::new(ui::components::ProcessList::new(s, r)), )); state.register_component(window); /* Keep track of the input mode. See ui::UIMode for details */ 'main: loop { - 'inner: loop { - /* Poll on all channels. Currently we have the input channel for stdin, watching events and the signal watcher. */ - select! { - recv(ticker) -> _ => { - state.redraw(true); - }, - recv(signal_recvr) -> sig => { - eprintln!("got signal {:?}", sig); - match sig.unwrap() { - signal_hook::SIGWINCH => { - state.update_size(); - state.render(); - state.redraw(true); - }, - _ => {} - } - }, - recv(receiver) -> msg => { - match msg.unwrap() { - ThreadEvent::Input(Key::Ctrl('z')) => { - state.switch_to_main_screen(); - //_thread_handler.join().expect("Couldn't join on the associated thread"); - let self_pid = nix::unistd::Pid::this(); - nix::sys::signal::kill(self_pid, nix::sys::signal::Signal::SIGSTOP).unwrap(); - state.switch_to_alternate_screen(); - state.restore_input(); - // BUG: thread sends input event after one received key - state.update_size(); - state.render(); - state.redraw(true); - }, - ThreadEvent::Input(k) => { - match k { - Key::Char('q') | Key::Char('Q') => { - drop(state); - break 'main; - }, - key => { - state.rcv_event(UIEvent::Input(key)); - state.redraw(false); - }, - } - }, - ThreadEvent::UIEvent(_) => { - }, - } - }, - } - } // end of 'inner + /* Poll on all channels. Currently we have the input channel for stdin, watching events and the signal watcher. */ + select! { + recv(ticker) -> _ => { + state.redraw(true); + }, + recv(signal_recvr) -> sig => { + eprintln!("got signal {:?}", sig); + match sig.unwrap() { + signal_hook::SIGWINCH => { + state.update_size(); + state.render(); + state.redraw(true); + }, + _ => {} + } + }, + recv(receiver) -> msg => { + match msg.unwrap() { + ThreadEvent::Input(Key::Ctrl('z')) => { + state.switch_to_main_screen(); + //_thread_handler.join().expect("Couldn't join on the associated thread"); + let self_pid = nix::unistd::Pid::this(); + nix::sys::signal::kill(self_pid, nix::sys::signal::Signal::SIGSTOP).unwrap(); + state.switch_to_alternate_screen(); + state.restore_input(); + // BUG: thread sends input event after one received key + state.update_size(); + state.render(); + state.redraw(true); + }, + ThreadEvent::Input(k) => { + match k { + Key::Char('q') | Key::Char('Q') => { + drop(state); + break 'main; + }, + key => { + state.rcv_event(UIEvent::Input(key)); + state.redraw(false); + }, + } + }, + } + }, + } } Ok(()) } diff --git a/src/thread.rs b/src/thread.rs new file mode 100644 index 0000000..c98d7f7 --- /dev/null +++ b/src/thread.rs @@ -0,0 +1,221 @@ +/* + * bb + * + * Copyright 2019 Manos Pitsidianakis + * + * This file is part of bb. + * + * bb is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * bb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with bb. If not, see <http://www.gnu.org/licenses/>. + */ + +use crate::ui::components::processes::State; +use crate::ui::components::*; +use crossbeam::channel::bounded; +use crossbeam::{Receiver, Sender}; +use std::collections::HashMap; +use std::fs::File; +use std::io::prelude::*; +use std::path::PathBuf; +use std::str::FromStr; + +struct ProcessData { + cpu_stat: Stat, + processes_times: HashMap<Pid, usize>, +} + +pub fn start_thread() -> (Sender<Vec<ProcessDisplay>>, Receiver<Vec<ProcessDisplay>>) { + let (s, r): (Sender<Vec<ProcessDisplay>>, Receiver<Vec<ProcessDisplay>>) = bounded(1); /* ours */ + let (ts, tr): (Sender<Vec<ProcessDisplay>>, Receiver<Vec<ProcessDisplay>>) = bounded(1); /* theirs */ + ts.send(Vec::with_capacity(1024)).unwrap(); + std::thread::spawn(move || { + let mut data = ProcessData { + cpu_stat: get_stat(&mut 0).remove(0), + processes_times: Default::default(), + }; + loop { + let mut processes = match tr.recv() { + Ok(n) => n, + Err(e) => panic!(e), + }; + processes.clear(); + + let cpu_stat = get_stat(&mut 0).remove(0); + for entry in std::fs::read_dir("/proc/").unwrap() { + let dir = entry.unwrap(); + if let Some(fname) = dir.file_name().to_str() { + if !fname.chars().all(|c| c.is_numeric()) { + continue; + } + } else { + continue; + } + + let process = if let Ok(p) = get_pid_info(dir.path()) { + p + } else { + continue; + }; + + if process.cmd_line.is_empty() { + /* This is a kernel thread, skip for now */ + continue; + } + + let mut process_display = ProcessDisplay { + i: process.pid, + pid: PidString(process.pid.to_string()), + ppid: PpidString(process.ppid.to_string()), + vm_rss: VmRssString(Bytes(process.vm_rss * 1024).as_convenient_string()), + cpu_percent: (100.0 + * ((process.utime + - data + .processes_times + .get(&process.pid) + .map(|v| *v) + .unwrap_or(process.utime)) as f64 + / ((cpu_stat.total_time() - data.cpu_stat.total_time()) as f64))) + as usize, + utime: process.utime, + state: process.state, + cmd_line: CmdLineString(process.cmd_line), + username: UserString(crate::ui::username(process.uid)), + }; + if process_display.cpu_percent > 100 { + process_display.cpu_percent = 0; + } + + data.processes_times + .insert(process.pid, process_display.utime); + + processes.push(process_display); + } + data.cpu_stat = cpu_stat; + + s.send(processes).unwrap(); + } + }); + + (ts, r) +} + +/* Might return Error if process has disappeared + * during the function's run */ +fn get_pid_info(mut path: PathBuf) -> Result<Process, std::io::Error> { + /* proc file structure can be found in man 5 proc.*/ + path.push("status"); + let mut file: File = File::open(&path)?; + let mut res = String::with_capacity(2048); + file.read_to_string(&mut res)?; + let mut lines_iter = res.lines(); + let mut ret = Process { + pid: 0, + ppid: 0, + vm_rss: 0, + uid: 0, + utime: 0, + state: State::Waiting, + cmd_line: String::new(), + }; + let mut line; + + macro_rules! err { + ($res:expr) => { + match $res { + Ok(v) => v, + Err(_) => { + return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "")); + } + } + }; + } + + macro_rules! none_err { + ($res:expr) => { + if let Some(v) = $res { + v + } else { + return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "")); + } + }; + } + + let mut b = 0; + while b < 5 { + let line_opt = lines_iter.next(); + if line_opt.is_none() { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + format!("{} returned malformed input", path.display()), + )); + } + line = none_err!(line_opt); + let mut mut_value_iter = line.split_whitespace(); + match mut_value_iter.next() { + Some("VmRSS:") => { + ret.vm_rss = err!(usize::from_str(none_err!(mut_value_iter.next()))); + b += 1; + } + Some("State:") => { + ret.state = State::from(none_err!(none_err!(mut_value_iter.next()).chars().next())); + b += 1; + } + Some("Pid:") => { + ret.pid = err!(i32::from_str(none_err!(mut_value_iter.next()))); + b += 1; + } + Some("PPid:") => { + ret.ppid = err!(i32::from_str(none_err!(mut_value_iter.next()))); + b += 1; + } + Some("Uid:") => { + ret.uid = err!(u32::from_str(none_err!(mut_value_iter.next()))); + b += 1; + } + None => { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + format!( + "{} returned malformed input. Original error was while parsing file", + path.display(), + ), + )); + } + _ => {} + } + } + + path.pop(); + path.push("cmdline"); + let mut file: File = File::open(&path)?; + res.clear(); + file.read_to_string(&mut res)?; + if !res.is_empty() { + /* values are separated by null bytes */ + ret.cmd_line = format!("{}", res.split('\0').collect::<Vec<&str>>().join(" ")); + } + path.pop(); + path.push("stat"); + let mut file: File = File::open(&path)?; + res.clear(); + file.read_to_string(&mut res)?; + /* values are separated by whitespace and are in a specific order */ + if !res.is_empty() { + let mut vals = res.split_whitespace().skip(13); + ret.utime = err!(usize::from_str(none_err!(vals.next()))); + ret.utime += err!(usize::from_str(none_err!(vals.next()))); + ret.utime += err!(usize::from_str(none_err!(vals.next()))); + ret.utime += err!(usize::from_str(none_err!(vals.next()))); + } + Ok(ret) +} @@ -1,22 +1,22 @@ /* - * meli - ui crate. + * bb * - * Copyright 2017-2018 Manos Pitsidianakis + * Copyright 2019 Manos Pitsidianakis * - * This file is part of meli. + * This file is part of bb. * - * meli is free software: you can redistribute it and/or modify + * bb is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * - * meli is distributed in the hope that it will be useful, + * bb is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with meli. If not, see <http://www.gnu.org/licenses/>. + * along with bb. If not, see <http://www.gnu.org/licenses/>. */ /*! diff --git a/src/ui/components.rs b/src/ui/components.rs index 086da95..9571c9b 100644 --- a/src/ui/components.rs +++ b/src/ui/components.rs @@ -1,22 +1,22 @@ /* - * meli - ui crate. + * bb * - * Copyright 2017-2018 Manos Pitsidianakis + * Copyright 2019 Manos Pitsidianakis * - * This file is part of meli. + * This file is part of bb. * - * meli is free software: you can redistribute it and/or modify + * bb is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * - * meli is distributed in the hope that it will be useful, + * bb is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with meli. If not, see <http://www.gnu.org/licenses/>. + * along with bb. If not, see <http://www.gnu.org/licenses/>. */ /*! @@ -26,48 +26,24 @@ See the `Component` Trait for more details. */ use super::*; -use crate::ui::terminal::*; -use crate::ui::types::*; mod utilities; pub use utilities::*; mod kernel; pub use kernel::*; -mod processes; +pub mod processes; pub use processes::*; use std::collections::{HashMap, VecDeque}; use std::fmt; use std::fmt::{Debug, Display}; -use super::{Key, StatusEvent, UIEvent}; +use super::{Key, UIEvent}; /// The upper and lower boundary char. const HORZ_BOUNDARY: char = '─'; /// The left and right boundary char. const VERT_BOUNDARY: char = '│'; -/// The top-left corner -const _TOP_LEFT_CORNER: char = '┌'; -/// The top-right corner -const _TOP_RIGHT_CORNER: char = '┐'; -/// The bottom-left corner -const _BOTTOM_LEFT_CORNER: char = '└'; -/// The bottom-right corner -const _BOTTOM_RIGHT_CORNER: char = '┘'; - -const LIGHT_VERTICAL_AND_RIGHT: char = '├'; - -const _LIGHT_VERTICAL_AND_LEFT: char = '┤'; - -const LIGHT_DOWN_AND_HORIZONTAL: char = '┬'; - -const LIGHT_UP_AND_HORIZONTAL: char = '┴'; - -const _DOUBLE_DOWN_AND_RIGHT: char = '╔'; -const _DOUBLE_DOWN_AND_LEFT: char = '╗'; -const _DOUBLE_UP_AND_LEFT: char = '╝'; -const _DOUBLE_UP_AND_RIGHT: char = '╚'; - pub type ShortcutMap = HashMap<&'static str, Key>; pub type ShortcutMaps = HashMap<String, ShortcutMap>; @@ -92,22 +68,6 @@ pub trait Component: Display + Debug + Send { } } -/* -pub(crate) fn is_box_char(ch: char) -> bool { - match ch { - HORZ_BOUNDARY | VERT_BOUNDARY => true, - _ => false, - } -} - - * pub(crate) fn is_box_char(ch: char) -> bool { - * match ch { - * '└' | '─' | '┘' | '┴' | '┌' | '│' | '├' | '┐' | '┬' | '┤' | '┼' | '╷' | '╵' | '╴' | '╶' => true, - * _ => false, - * } - * } - */ - fn bin_to_ch(b: u32) -> char { match b { 0b0001 => '╶', @@ -376,19 +336,20 @@ pub fn create_box(grid: &mut CellBuffer, area: Area) { let upper_left = upper_left!(area); let bottom_right = bottom_right!(area); - for x in get_x(upper_left)..get_x(bottom_right) { - grid[(x, get_y(upper_left))].set_ch(HORZ_BOUNDARY); - grid[(x, get_y(bottom_right))].set_ch(HORZ_BOUNDARY); - grid[(x, get_y(bottom_right))].set_fg(Color::Byte(240)); + for x in get_x(upper_left)..=get_x(bottom_right) { + //grid[(x, get_y(upper_left))].set_ch(HORZ_BOUNDARY); + //grid[(x, get_y(bottom_right))].set_ch(HORZ_BOUNDARY); + //grid[(x, get_y(bottom_right))].set_ch('▒'); + //grid[(x, get_y(bottom_right))].set_fg(Color::Byte(240)); } - for y in get_y(upper_left)..get_y(bottom_right) { - grid[(get_x(upper_left), y)].set_ch(VERT_BOUNDARY); - grid[(get_x(bottom_right), y)].set_ch(VERT_BOUNDARY); + for y in get_y(upper_left)..=get_y(bottom_right) { + //grid[(get_x(upper_left), y)].set_ch(VERT_BOUNDARY); + grid[(get_x(bottom_right), y)].set_ch('▒'); grid[(get_x(bottom_right), y)].set_fg(Color::Byte(240)); } - set_and_join_box(grid, upper_left, HORZ_BOUNDARY); - set_and_join_box(grid, set_x(upper_left, get_x(bottom_right)), HORZ_BOUNDARY); - set_and_join_box(grid, set_y(upper_left, get_y(bottom_right)), VERT_BOUNDARY); - set_and_join_box(grid, bottom_right, VERT_BOUNDARY); + //set_and_join_box(grid, upper_left, HORZ_BOUNDARY); + //set_and_join_box(grid, set_x(upper_left, get_x(bottom_right)), HORZ_BOUNDARY); + //set_and_join_box(grid, set_y(upper_left, get_y(bottom_right)), VERT_BOUNDARY); + //set_and_join_box(grid, bottom_right, VERT_BOUNDARY); } diff --git a/src/ui/components/kernel.rs b/src/ui/components/kernel.rs index c2f7ae5..710454c 100644 --- a/src/ui/components/kernel.rs +++ b/src/ui/components/kernel.rs @@ -1,3 +1,24 @@ +/* + * bb + * + * Copyright 2019 Manos Pitsidianakis + * + * This file is part of bb. + * + * bb is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * bb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with bb. If not, see <http://www.gnu.org/licenses/>. + */ + use super::*; use std::fs::File; use std::io::prelude::*; @@ -61,6 +82,8 @@ impl Component for KernelMetrics { let bottom_right = bottom_right!(area); let total_rows = height!(area); let total_cols = width!(area); + + let show_all_cores: bool = total_rows > 1 /* hostname, kernel version, uptime row */ + 1 /* CPU total bar row */ + 0 /* padding rows */ + 1 /* RAM row */; dirty_areas.push_back(area); if self.dirty { clear_area(grid, area); @@ -82,7 +105,7 @@ impl Component for KernelMetrics { ((x + 2, y), bottom_right), false, ); - let (x, y) = write_string_to_grid( + write_string_to_grid( &self.kernel, grid, Color::Default, @@ -139,6 +162,9 @@ impl Component for KernelMetrics { let mut boot_time: usize = 0; for (i, cpu_stat) in get_stat(&mut boot_time).into_iter().enumerate() { let (mut x, y) = if i > 0 { + if !show_all_cores { + break; + } write_string_to_grid( &format!("CPU{}", i), grid, @@ -182,6 +208,9 @@ impl Component for KernelMetrics { .total_time() .saturating_sub(self.cpu_stat[i].total_time())) as f64) * bar_max as f64) as usize; + if bar_length >= width!(area) { + return; + } let mut x_offset = 0; while x_offset < bar_length { @@ -216,8 +245,6 @@ impl Component for KernelMetrics { /* Draw RAM usage bar */ - y_offset += 1; - let bar_max = bar_max + 6; let (available, total) = get_mem_info(); let available_length = ((available as f64 / total as f64) * bar_max as f64) as usize; @@ -294,6 +321,9 @@ impl Component for KernelMetrics { /* CPU Times */ let mut cpu_column_width = "CPU".len(); let upper_left = pos_inc(upper_left, (bar_max + 5, 2)); + if get_x(upper_left) >= get_x(bottom_right) { + return; + } write_string_to_grid( "CPU%", grid, @@ -333,8 +363,10 @@ impl Component for KernelMetrics { } /* Load average */ - let mut load_column_width = "LOAD_AVG".len(); let upper_left = pos_inc(upper_left, (cpu_column_width + 3, 0)); + if get_x(upper_left) >= get_x(bottom_right) { + return; + } write_string_to_grid( "LOAD_AVG", grid, @@ -435,8 +467,10 @@ fn get_cpu_times( macro_rules! val { ($tag:literal, $field:tt) => { - let percent = (cpu_stat.$field - old_cpu_stat.$field) as f64 - / (cpu_stat.total_time() - old_cpu_stat.total_time()) as f64; + let percent = (cpu_stat.$field.saturating_sub(old_cpu_stat.$field)) as f64 + / (cpu_stat + .total_time() + .saturating_sub(old_cpu_stat.total_time())) as f64; let s = format!("{:.1}%", percent * 100.0); ret.push(( $tag, diff --git a/src/ui/components/processes.rs b/src/ui/components/processes.rs index 658d65a..6d6ae5c 100644 --- a/src/ui/components/processes.rs +++ b/src/ui/components/processes.rs @@ -1,12 +1,64 @@ +/* + * bb + * + * Copyright 2019 Manos Pitsidianakis + * + * This file is part of bb. + * + * bb is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * bb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with bb. If not, see <http://www.gnu.org/licenses/>. + */ + use super::*; -use std::fs::File; -use std::io::prelude::*; -use std::path::PathBuf; -use std::str::FromStr; +use crossbeam::{Receiver, Sender}; + +const SIGNAL_LIST: &[(i32, &'static str)] = &[ + (1, "1 HUP"), + (2, "2 INT"), + (3, "3 QUIT"), + (4, "4 ILL"), + (5, "5 TRAP"), + (6, "6 ABRT"), + (7, "7 BUS"), + (8, "8 FPE"), + (9, "9 KILL"), + (10, "10 USR1"), + (11, "11 SEGV"), + (12, "12 USR2"), + (13, "13 PIPE"), + (14, "14 ALRM"), + (15, "15 TERM"), + (16, "16 STKFLT"), + (17, "17 CHLD"), + (18, "18 CONT"), + (19, "19 STOP"), + (20, "20 TSTP"), + (21, "21 TTIN"), + (22, "22 TTOU"), + (23, "23 URG"), + (24, "24 XCPU"), + (25, "25 XFSZ"), + (26, "26 VTALRM"), + (27, "27 PROF"), + (28, "28 WINCH"), + (29, "29 POLL"), + (30, "30 PWR"), + (31, "31 SYS"), +]; /* Hold maximum width for each column */ #[derive(Debug)] -struct ColumnWidthMaxima { +pub struct ColumnWidthMaxima { pid: usize, ppid: usize, vm_rss: usize, @@ -21,7 +73,7 @@ impl ColumnWidthMaxima { pid: "PID".len(), ppid: "PPID".len(), vm_rss: "VM_RSS".len(), - cpu_percent: "CPU%".len(), + cpu_percent: " CPU%".len(), state: 1, username: "USER".len(), } @@ -32,10 +84,11 @@ macro_rules! define_column_string { ($($typename: tt),+) => { $( #[derive(Debug)] - struct $typename(String); + pub struct $typename(pub String); impl $typename { - fn len(&self) -> usize { + #[allow(dead_code)] + pub fn len(&self) -> usize { self.0.len() } } @@ -60,28 +113,29 @@ define_column_string!( UserString ); -type Pid = usize; +pub type Pid = i32; /* Wrapper type for display strings */ #[derive(Debug)] -struct ProcessDisplay { - i: Pid, - pid: PidString, - ppid: PpidString, - vm_rss: VmRssString, - cpu_percent: usize, - state: State, - cmd_line: CmdLineString, - username: UserString, - utime: usize, +pub struct ProcessDisplay { + pub i: Pid, + pub pid: PidString, + pub ppid: PpidString, + pub vm_rss: VmRssString, + pub cpu_percent: usize, + pub state: State, + pub cmd_line: CmdLineString, + pub username: UserString, + pub utime: usize, } /* process list components */ #[derive(Debug)] pub struct ProcessList { + sender: Sender<Vec<ProcessDisplay>>, + rcver: Receiver<Vec<ProcessDisplay>>, page_movement: Option<PageMovement>, cpu_stat: Stat, - pid_max: usize, cursor: usize, height: usize, dirty: bool, @@ -90,10 +144,19 @@ pub struct ProcessList { freeze: bool, processes_times: HashMap<Pid, usize>, processes: Vec<ProcessDisplay>, + mode: ProcessListMode, +} + +#[derive(Debug, PartialEq)] +enum ProcessListMode { + Normal, + Kill(u16), } +use ProcessListMode::*; + #[derive(Debug, PartialEq)] -enum State { +pub enum State { /* Z Zombie */ Zombie, /* R Running */ @@ -106,16 +169,8 @@ enum State { Stopped, /* t Tracing stop (Linux 2.6.33 onward) */ Tracing, - /*W Paging (only before Linux 2.6.0) */ - Paging, /* X Dead (from Linux 2.6.0 onward) */ Dead, - /* K Wakekill (Linux 2.6.33 to 3.13 only) */ - Wakekill, - /* W Waking (Linux 2.6.33 to 3.13 only) */ - Waking, - /* P Parked (Linux 3.9 to 3.13 only) */ - Parked, } impl From<char> for State { @@ -128,12 +183,8 @@ impl From<char> for State { 'Z' => State::Zombie, 'T' => State::Stopped, 't' => State::Tracing, - 'W' => State::Paging, 'X' => State::Dead, 'x' => State::Dead, - 'K' => State::Wakekill, - 'W' => State::Waking, - 'P' => State::Parked, _ => unreachable!(), } } @@ -151,26 +202,20 @@ impl std::fmt::Display for State { State::Zombie => 'Z', State::Stopped => 'T', State::Tracing => 't', - State::Paging => 'W', State::Dead => 'X', - State::Dead => 'x', - State::Wakekill => 'K', - State::Waking => 'W', - State::Parked => 'P', - _ => unreachable!(), } ) } } -struct Process { - pid: usize, - ppid: usize, - vm_rss: usize, - state: State, - uid: u32, - cmd_line: String, - utime: usize, +pub struct Process { + pub pid: i32, + pub ppid: i32, + pub vm_rss: usize, + pub state: State, + pub uid: u32, + pub cmd_line: String, + pub utime: usize, } impl fmt::Display for ProcessList { @@ -180,20 +225,19 @@ impl fmt::Display for ProcessList { } impl ProcessList { - pub fn new() |