summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorManos Pitsidianakis <el13635@mail.ntua.gr>2019-09-04 17:39:36 +0300
committerManos Pitsidianakis <el13635@mail.ntua.gr>2019-09-04 20:07:02 +0300
commitf759b54a54c48ea46388f01c1d550e3e24f81afd (patch)
treeff9982e5323105ad76f209c6a153361d5d739d3e /src
parente9bab5453d003de26e2b2ba81277afa676eba67e (diff)
first version
Diffstat (limited to 'src')
-rw-r--r--src/main.rs106
-rw-r--r--src/thread.rs221
-rw-r--r--src/ui.rs12
-rw-r--r--src/ui/components.rs79
-rw-r--r--src/ui/components/kernel.rs46
-rw-r--r--src/ui/components/processes.rs517
-rw-r--r--src/ui/components/utilities.rs356
-rw-r--r--src/ui/components/utilities/widgets.rs69
-rw-r--r--src/ui/state.rs21
-rw-r--r--src/ui/terminal.rs12
-rw-r--r--src/ui/terminal/cells.rs169
-rw-r--r--src/ui/terminal/keys.rs17
-rw-r--r--src/ui/terminal/position.rs12
-rw-r--r--src/ui/text_processing.rs21
-rw-r--r--src/ui/text_processing/grapheme_clusters.rs21
-rw-r--r--src/ui/text_processing/line_break.rs21
-rw-r--r--src/ui/text_processing/tables.rs21
-rw-r--r--src/ui/text_processing/types.rs21
-rw-r--r--src/ui/text_processing/wcwidth.rs21
-rw-r--r--src/ui/types.rs39
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)
+}
diff --git a/src/ui.rs b/src/ui.rs
index a01e7e6..a7bd79b 100644
--- a/src/ui.rs
+++ b/src/ui.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/>.
*/
/*!
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()