Added dialog for dd, added error message if fail to dd, cleaned up some stuff

This commit is contained in:
ClementTsang 2020-01-01 23:39:47 -05:00
parent 7208908413
commit b22c07aba2
5 changed files with 289 additions and 168 deletions

View File

@ -2,7 +2,7 @@ pub mod data_collection;
use data_collection::{processes, temperature}; use data_collection::{processes, temperature};
use std::time::Instant; use std::time::Instant;
use crate::constants; use crate::{canvas, constants, data_conversion::ConvertedProcessData, utils::error::Result};
mod process_killer; mod process_killer;
@ -47,10 +47,14 @@ pub struct App {
second_char: char, second_char: char,
pub use_dot: bool, pub use_dot: bool,
pub show_help: bool, pub show_help: bool,
pub show_dd: bool,
pub dd_err: Option<String>,
to_delete_process: Option<ConvertedProcessData>,
pub is_frozen: bool, pub is_frozen: bool,
pub left_legend: bool, pub left_legend: bool,
pub use_current_cpu_total: bool, pub use_current_cpu_total: bool,
last_key_press: Instant, last_key_press: Instant,
pub canvas_data: canvas::CanvasData,
} }
impl App { impl App {
@ -80,15 +84,23 @@ impl App {
second_char: ' ', second_char: ' ',
use_dot, use_dot,
show_help: false, show_help: false,
show_dd: false,
dd_err: None,
to_delete_process: None,
is_frozen: false, is_frozen: false,
left_legend, left_legend,
use_current_cpu_total, use_current_cpu_total,
last_key_press: Instant::now(), last_key_press: Instant::now(),
canvas_data: canvas::CanvasData::default(),
} }
} }
pub fn reset(&mut self) { pub fn reset(&mut self) {
self.reset_multi_tap_keys(); self.reset_multi_tap_keys();
self.show_help = false;
self.show_dd = false;
self.to_delete_process = None;
self.dd_err = None;
} }
fn reset_multi_tap_keys(&mut self) { fn reset_multi_tap_keys(&mut self) {
@ -96,18 +108,30 @@ impl App {
self.second_char = ' '; self.second_char = ' ';
} }
pub fn on_enter(&mut self) {} fn is_in_dialog(&self) -> bool {
self.show_help || self.show_dd
pub fn on_esc(&mut self) {
if self.show_help {
self.show_help = false;
}
self.reset_multi_tap_keys();
} }
// TODO: How should we make it for process panel specific hotkeys? Only if we're on process panel? Or what? /// One of two functions allowed to run while in a dialog...
pub fn on_key(&mut self, caught_char: char) { pub fn on_enter(&mut self) {
if !self.show_help { if self.show_dd {
// If within dd...
if self.dd_err.is_none() {
// Also ensure that we didn't just fail a dd...
let dd_result = self.kill_highlighted_process();
if let Err(dd_err) = dd_result {
// There was an issue... inform the user...
self.dd_err = Some(dd_err.to_string());
} else {
self.show_dd = false;
}
}
}
}
pub fn on_char_key(&mut self, caught_char: char) {
// Forbid any char key presses when showing a dialog box...
if !self.is_in_dialog() {
let current_key_press_inst = Instant::now(); let current_key_press_inst = Instant::now();
if current_key_press_inst.duration_since(self.last_key_press).as_millis() > constants::MAX_KEY_TIMEOUT_IN_MILLISECONDS { if current_key_press_inst.duration_since(self.last_key_press).as_millis() > constants::MAX_KEY_TIMEOUT_IN_MILLISECONDS {
self.reset_multi_tap_keys(); self.reset_multi_tap_keys();
@ -120,7 +144,11 @@ impl App {
if self.awaiting_second_char && self.second_char == 'd' { if self.awaiting_second_char && self.second_char == 'd' {
self.awaiting_second_char = false; self.awaiting_second_char = false;
self.second_char = ' '; self.second_char = ' ';
self.kill_highlighted_process().unwrap_or(()); // TODO: Return error to user? We have a dialog box...
let current_process = &self.canvas_data.process_data[self.currently_selected_process_position as usize];
self.to_delete_process = Some(current_process.clone());
self.show_dd = true;
self.reset_multi_tap_keys();
} else { } else {
self.awaiting_second_char = true; self.awaiting_second_char = true;
self.second_char = 'd'; self.second_char = 'd';
@ -196,12 +224,21 @@ impl App {
} }
} }
fn kill_highlighted_process(&self) -> crate::utils::error::Result<()> { pub fn kill_highlighted_process(&mut self) -> Result<()> {
let current_pid = u64::from(self.data.list_of_processes[self.currently_selected_process_position as usize].pid); // Technically unnecessary but this is a good check...
process_killer::kill_process_given_pid(current_pid)?; if let ApplicationPosition::Process = self.current_application_position {
if let Some(current_selected_process) = &(self.to_delete_process) {
process_killer::kill_process_given_pid(current_selected_process.pid)?;
}
self.to_delete_process = None;
}
Ok(()) Ok(())
} }
pub fn get_current_highlighted_process(&self) -> Option<ConvertedProcessData> {
self.to_delete_process.clone()
}
// For now, these are hard coded --- in the future, they shouldn't be! // For now, these are hard coded --- in the future, they shouldn't be!
// //
// General idea for now: // General idea for now:
@ -212,6 +249,7 @@ impl App {
// Network -(up)> MEM, -(right)> PROC // Network -(up)> MEM, -(right)> PROC
// PROC -(up)> Disk, -(left)> Network // PROC -(up)> Disk, -(left)> Network
pub fn on_left(&mut self) { pub fn on_left(&mut self) {
if !self.is_in_dialog() {
self.current_application_position = match self.current_application_position { self.current_application_position = match self.current_application_position {
ApplicationPosition::Process => ApplicationPosition::Network, ApplicationPosition::Process => ApplicationPosition::Network,
ApplicationPosition::Disk => ApplicationPosition::Mem, ApplicationPosition::Disk => ApplicationPosition::Mem,
@ -220,8 +258,10 @@ impl App {
}; };
self.reset_multi_tap_keys(); self.reset_multi_tap_keys();
} }
}
pub fn on_right(&mut self) { pub fn on_right(&mut self) {
if !self.is_in_dialog() {
self.current_application_position = match self.current_application_position { self.current_application_position = match self.current_application_position {
ApplicationPosition::Mem => ApplicationPosition::Temp, ApplicationPosition::Mem => ApplicationPosition::Temp,
ApplicationPosition::Network => ApplicationPosition::Process, ApplicationPosition::Network => ApplicationPosition::Process,
@ -229,8 +269,10 @@ impl App {
}; };
self.reset_multi_tap_keys(); self.reset_multi_tap_keys();
} }
}
pub fn on_up(&mut self) { pub fn on_up(&mut self) {
if !self.is_in_dialog() {
self.current_application_position = match self.current_application_position { self.current_application_position = match self.current_application_position {
ApplicationPosition::Mem => ApplicationPosition::Cpu, ApplicationPosition::Mem => ApplicationPosition::Cpu,
ApplicationPosition::Network => ApplicationPosition::Mem, ApplicationPosition::Network => ApplicationPosition::Mem,
@ -241,8 +283,10 @@ impl App {
}; };
self.reset_multi_tap_keys(); self.reset_multi_tap_keys();
} }
}
pub fn on_down(&mut self) { pub fn on_down(&mut self) {
if !self.is_in_dialog() {
self.current_application_position = match self.current_application_position { self.current_application_position = match self.current_application_position {
ApplicationPosition::Cpu => ApplicationPosition::Mem, ApplicationPosition::Cpu => ApplicationPosition::Mem,
ApplicationPosition::Mem => ApplicationPosition::Network, ApplicationPosition::Mem => ApplicationPosition::Network,
@ -252,30 +296,47 @@ impl App {
}; };
self.reset_multi_tap_keys(); self.reset_multi_tap_keys();
} }
}
pub fn skip_to_first(&mut self) { pub fn skip_to_first(&mut self) {
if !self.is_in_dialog() {
match self.current_application_position { match self.current_application_position {
ApplicationPosition::Process => self.currently_selected_process_position = 0, ApplicationPosition::Process => self.currently_selected_process_position = 0,
ApplicationPosition::Temp => self.currently_selected_temperature_position = 0, ApplicationPosition::Temp => self.currently_selected_temperature_position = 0,
ApplicationPosition::Disk => self.currently_selected_disk_position = 0, ApplicationPosition::Disk => self.currently_selected_disk_position = 0,
ApplicationPosition::Cpu => self.currently_selected_cpu_table_position = 0,
_ => {} _ => {}
} }
self.scroll_direction = ScrollDirection::UP; self.scroll_direction = ScrollDirection::UP;
self.reset_multi_tap_keys(); self.reset_multi_tap_keys();
} }
}
pub fn skip_to_last(&mut self) { pub fn skip_to_last(&mut self) {
if !self.is_in_dialog() {
match self.current_application_position { match self.current_application_position {
ApplicationPosition::Process => self.currently_selected_process_position = self.data.list_of_processes.len() as i64 - 1, ApplicationPosition::Process => self.currently_selected_process_position = self.data.list_of_processes.len() as i64 - 1,
ApplicationPosition::Temp => self.currently_selected_temperature_position = self.data.list_of_temperature_sensor.len() as i64 - 1, ApplicationPosition::Temp => self.currently_selected_temperature_position = self.data.list_of_temperature_sensor.len() as i64 - 1,
ApplicationPosition::Disk => self.currently_selected_disk_position = self.data.list_of_disks.len() as i64 - 1, ApplicationPosition::Disk => self.currently_selected_disk_position = self.data.list_of_disks.len() as i64 - 1,
ApplicationPosition::Cpu => {
if let Some(cpu_package) = self.data.list_of_cpu_packages.last() {
if self.show_average_cpu {
self.currently_selected_cpu_table_position = cpu_package.cpu_vec.len() as i64;
} else {
self.currently_selected_cpu_table_position = cpu_package.cpu_vec.len() as i64 - 1;
}
}
}
_ => {} _ => {}
} }
self.scroll_direction = ScrollDirection::DOWN; self.scroll_direction = ScrollDirection::DOWN;
self.reset_multi_tap_keys(); self.reset_multi_tap_keys();
} }
}
pub fn decrement_position_count(&mut self) { pub fn decrement_position_count(&mut self) {
if !self.is_in_dialog() {
match self.current_application_position { match self.current_application_position {
ApplicationPosition::Process => self.change_process_position(-1), ApplicationPosition::Process => self.change_process_position(-1),
ApplicationPosition::Temp => self.change_temp_position(-1), ApplicationPosition::Temp => self.change_temp_position(-1),
@ -286,8 +347,10 @@ impl App {
self.scroll_direction = ScrollDirection::UP; self.scroll_direction = ScrollDirection::UP;
self.reset_multi_tap_keys(); self.reset_multi_tap_keys();
} }
}
pub fn increment_position_count(&mut self) { pub fn increment_position_count(&mut self) {
if !self.is_in_dialog() {
match self.current_application_position { match self.current_application_position {
ApplicationPosition::Process => self.change_process_position(1), ApplicationPosition::Process => self.change_process_position(1),
ApplicationPosition::Temp => self.change_temp_position(1), ApplicationPosition::Temp => self.change_temp_position(1),
@ -298,6 +361,7 @@ impl App {
self.scroll_direction = ScrollDirection::DOWN; self.scroll_direction = ScrollDirection::DOWN;
self.reset_multi_tap_keys(); self.reset_multi_tap_keys();
} }
}
fn change_cpu_table_position(&mut self, num_to_change_by: i64) { fn change_cpu_table_position(&mut self, num_to_change_by: i64) {
if let Some(cpu_package) = self.data.list_of_cpu_packages.last() { if let Some(cpu_package) = self.data.list_of_cpu_packages.last() {

View File

@ -1,8 +1,7 @@
/// This file is meant to house (OS specific) implementations on how to kill processes. /// This file is meant to house (OS specific) implementations on how to kill processes.
use crate::utils::error::{BottomError, Result};
use std::process::Command; use std::process::Command;
// TODO: Make it update process list on freeze.
// Copied from SO: https://stackoverflow.com/a/55231715 // Copied from SO: https://stackoverflow.com/a/55231715
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
use winapi::{ use winapi::{
@ -33,7 +32,7 @@ impl Process {
} }
/// Kills a process, given a PID. /// Kills a process, given a PID.
pub fn kill_process_given_pid(pid: u64) -> crate::utils::error::Result<()> { pub fn kill_process_given_pid(pid: u32) -> Result<()> {
if cfg!(target_os = "linux") { if cfg!(target_os = "linux") {
// Linux // Linux
Command::new("kill").arg(pid.to_string()).output()?; Command::new("kill").arg(pid.to_string()).output()?;
@ -45,10 +44,14 @@ pub fn kill_process_given_pid(pid: u64) -> crate::utils::error::Result<()> {
} else if cfg!(target_os = "macos") { } else if cfg!(target_os = "macos") {
// TODO: macOS // TODO: macOS
// See how sysinfo does it... https://docs.rs/sysinfo/0.9.5/sysinfo/trait.ProcessExt.html // See how sysinfo does it... https://docs.rs/sysinfo/0.9.5/sysinfo/trait.ProcessExt.html
debug!("Sorry, macOS support is not implemented yet!"); return Err(BottomError::GenericError {
message: "Sorry, macOS support is not implemented yet!".to_string(),
});
} else { } else {
// TODO: Others? // TODO: Others?
debug!("Sorry, other support this is not implemented yet!"); return Err(BottomError::GenericError {
message: "Sorry, support operating systems outside the main three is not implemented yet!".to_string(),
});
} }
Ok(()) Ok(())

View File

@ -1,4 +1,4 @@
use crate::{app, constants, utils::error, utils::gen_util::*}; use crate::{app, constants, data_conversion::ConvertedProcessData, utils::error, utils::gen_util::*};
use tui::{ use tui::{
backend, backend,
layout::{Alignment, Constraint, Direction, Layout, Rect}, layout::{Alignment, Constraint, Direction, Layout, Rect},
@ -46,7 +46,7 @@ pub struct CanvasData {
pub network_data_tx: Vec<(f64, f64)>, pub network_data_tx: Vec<(f64, f64)>,
pub disk_data: Vec<Vec<String>>, pub disk_data: Vec<Vec<String>>,
pub temp_sensor_data: Vec<Vec<String>>, pub temp_sensor_data: Vec<Vec<String>>,
pub process_data: Vec<Vec<String>>, pub process_data: Vec<ConvertedProcessData>,
pub memory_labels: Vec<(u64, u64)>, pub memory_labels: Vec<(u64, u64)>,
pub mem_data: Vec<(f64, f64)>, pub mem_data: Vec<(f64, f64)>,
pub swap_data: Vec<(f64, f64)>, pub swap_data: Vec<(f64, f64)>,
@ -93,11 +93,11 @@ fn gen_n_colours(num_to_gen: i32) -> Vec<Color> {
colour_vec colour_vec
} }
pub fn draw_data<B: backend::Backend>(terminal: &mut Terminal<B>, app_state: &mut app::App, canvas_data: &CanvasData) -> error::Result<()> { pub fn draw_data<B: backend::Backend>(terminal: &mut Terminal<B>, app_state: &mut app::App) -> error::Result<()> {
terminal.autoresize()?; terminal.autoresize()?;
terminal.draw(|mut f| { terminal.draw(|mut f| {
if app_state.show_help { if app_state.show_help {
// Only for the dialog (help, dd) menus // Only for the help
let vertical_dialog_chunk = Layout::default() let vertical_dialog_chunk = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.margin(1) .margin(1)
@ -116,6 +116,52 @@ pub fn draw_data<B: backend::Backend>(terminal: &mut Terminal<B>, app_state: &mu
.alignment(Alignment::Left) .alignment(Alignment::Left)
.wrap(true) .wrap(true)
.render(&mut f, middle_dialog_chunk[1]); .render(&mut f, middle_dialog_chunk[1]);
} else if app_state.show_dd {
let vertical_dialog_chunk = Layout::default()
.direction(Direction::Vertical)
.margin(1)
.constraints([Constraint::Percentage(40), Constraint::Percentage(20), Constraint::Percentage(40)].as_ref())
.split(f.size());
let middle_dialog_chunk = Layout::default()
.direction(Direction::Horizontal)
.margin(0)
.constraints([Constraint::Percentage(30), Constraint::Percentage(40), Constraint::Percentage(30)].as_ref())
.split(vertical_dialog_chunk[1]);
if let Some(dd_err) = app_state.dd_err.clone() {
let dd_text = [Text::raw(format!("\nFailure to properly kill the process - {}", dd_err))];
Paragraph::new(dd_text.iter())
.block(Block::default().title("Kill Process Error (Press Esc to close)").borders(Borders::ALL))
.style(Style::default().fg(Color::Gray))
.alignment(Alignment::Center)
.wrap(true)
.render(&mut f, middle_dialog_chunk[1]);
} else if let Some(process) = app_state.get_current_highlighted_process() {
let dd_text = [
Text::raw(format!(
"\nAre you sure you want to kill process {} with PID {}?",
process.name, process.pid
)),
Text::raw("\n\nPress ENTER to proceed, ESC to exit."),
Text::raw("\nNote that if bottom is frozen, it must be unfrozen for changes to be shown."),
];
Paragraph::new(dd_text.iter())
.block(
Block::default()
.title("Kill Process Confirmation (Press Esc to close)")
.borders(Borders::ALL),
)
.style(Style::default().fg(Color::Gray))
.alignment(Alignment::Center)
.wrap(true)
.render(&mut f, middle_dialog_chunk[1]);
} else {
// This is a bit nasty, but it works well... I guess.
app_state.show_dd = false;
}
} else { } else {
let vertical_chunks = Layout::default() let vertical_chunks = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
@ -174,27 +220,18 @@ pub fn draw_data<B: backend::Backend>(terminal: &mut Terminal<B>, app_state: &mu
// Set up blocks and their components // Set up blocks and their components
// CPU graph // CPU graph
draw_cpu_graph(&mut f, &app_state, &canvas_data.cpu_data, cpu_chunk[graph_index]); draw_cpu_graph(&mut f, &app_state, cpu_chunk[graph_index]);
// CPU legend // CPU legend
draw_cpu_legend(&mut f, app_state, &canvas_data.cpu_data, cpu_chunk[legend_index]); draw_cpu_legend(&mut f, app_state, cpu_chunk[legend_index]);
//Memory usage graph //Memory usage graph
draw_memory_graph( draw_memory_graph(&mut f, &app_state, middle_chunks[0]);
&mut f,
&app_state,
&canvas_data.memory_labels,
&canvas_data.mem_data,
&canvas_data.swap_data,
middle_chunks[0],
);
// Network graph // Network graph
draw_network_graph( draw_network_graph(
&mut f, &mut f,
&app_state, &app_state,
&canvas_data.network_data_rx,
&canvas_data.network_data_tx,
if cfg!(not(target_os = "windows")) { if cfg!(not(target_os = "windows")) {
network_chunk[0] network_chunk[0]
} else { } else {
@ -202,45 +239,26 @@ pub fn draw_data<B: backend::Backend>(terminal: &mut Terminal<B>, app_state: &mu
}, },
); );
if cfg!(not(target_os = "windows")) { draw_network_labels(&mut f, app_state, network_chunk[1]);
draw_network_labels(
&mut f,
app_state,
canvas_data.rx_display.clone(),
canvas_data.tx_display.clone(),
canvas_data.total_rx_display.clone(),
canvas_data.total_tx_display.clone(),
network_chunk[1],
);
} else {
draw_network_labels(
&mut f,
app_state,
canvas_data.rx_display.clone(),
canvas_data.tx_display.clone(),
"N/A".to_string(),
"N/A".to_string(),
network_chunk[1],
);
}
// Temperature table // Temperature table
draw_temp_table(&mut f, app_state, &canvas_data.temp_sensor_data, middle_divided_chunk_2[0]); draw_temp_table(&mut f, app_state, middle_divided_chunk_2[0]);
// Disk usage table // Disk usage table
draw_disk_table(&mut f, app_state, &canvas_data.disk_data, middle_divided_chunk_2[1]); draw_disk_table(&mut f, app_state, middle_divided_chunk_2[1]);
// Processes table // Processes table
draw_processes_table(&mut f, app_state, &canvas_data.process_data, bottom_chunks[1]); draw_processes_table(&mut f, app_state, bottom_chunks[1]);
} }
})?; })?;
Ok(()) Ok(())
} }
fn draw_cpu_graph<B: backend::Backend>(f: &mut Frame<B>, app_state: &app::App, cpu_data: &[(String, Vec<(f64, f64)>)], draw_loc: Rect) { fn draw_cpu_graph<B: backend::Backend>(f: &mut Frame<B>, app_state: &app::App, draw_loc: Rect) {
// CPU usage graph let cpu_data: &[(String, Vec<(f64, f64)>)] = &app_state.canvas_data.cpu_data;
// CPU usage graph
let x_axis: Axis<String> = Axis::default() let x_axis: Axis<String> = Axis::default()
.style(Style::default().fg(GRAPH_COLOUR)) .style(Style::default().fg(GRAPH_COLOUR))
.bounds([0.0, constants::TIME_STARTS_FROM as f64 * 10.0]); .bounds([0.0, constants::TIME_STARTS_FROM as f64 * 10.0]);
@ -296,7 +314,9 @@ fn draw_cpu_graph<B: backend::Backend>(f: &mut Frame<B>, app_state: &app::App, c
.render(f, draw_loc); .render(f, draw_loc);
} }
fn draw_cpu_legend<B: backend::Backend>(f: &mut Frame<B>, app_state: &mut app::App, cpu_data: &[(String, Vec<(f64, f64)>)], draw_loc: Rect) { fn draw_cpu_legend<B: backend::Backend>(f: &mut Frame<B>, app_state: &mut app::App, draw_loc: Rect) {
let cpu_data: &[(String, Vec<(f64, f64)>)] = &(app_state.canvas_data.cpu_data);
let num_rows = i64::from(draw_loc.height) - 4; let num_rows = i64::from(draw_loc.height) - 4;
let start_position = get_start_position( let start_position = get_start_position(
num_rows, num_rows,
@ -345,9 +365,11 @@ fn draw_cpu_legend<B: backend::Backend>(f: &mut Frame<B>, app_state: &mut app::A
.render(f, draw_loc); .render(f, draw_loc);
} }
fn draw_memory_graph<B: backend::Backend>( fn draw_memory_graph<B: backend::Backend>(f: &mut Frame<B>, app_state: &app::App, draw_loc: Rect) {
f: &mut Frame<B>, app_state: &app::App, memory_labels: &[(u64, u64)], mem_data: &[(f64, f64)], swap_data: &[(f64, f64)], draw_loc: Rect, let mem_data: &[(f64, f64)] = &(app_state.canvas_data.mem_data);
) { let swap_data: &[(f64, f64)] = &(app_state.canvas_data.swap_data);
let memory_labels: &[(u64, u64)] = &(app_state.canvas_data.memory_labels);
let x_axis: Axis<String> = Axis::default() let x_axis: Axis<String> = Axis::default()
.style(Style::default().fg(GRAPH_COLOUR)) .style(Style::default().fg(GRAPH_COLOUR))
.bounds([0.0, constants::TIME_STARTS_FROM as f64 * 10.0]); .bounds([0.0, constants::TIME_STARTS_FROM as f64 * 10.0]);
@ -408,9 +430,10 @@ fn draw_memory_graph<B: backend::Backend>(
.render(f, draw_loc); .render(f, draw_loc);
} }
fn draw_network_graph<B: backend::Backend>( fn draw_network_graph<B: backend::Backend>(f: &mut Frame<B>, app_state: &app::App, draw_loc: Rect) {
f: &mut Frame<B>, app_state: &app::App, network_data_rx: &[(f64, f64)], network_data_tx: &[(f64, f64)], draw_loc: Rect, let network_data_rx: &[(f64, f64)] = &(app_state.canvas_data.network_data_rx);
) { let network_data_tx: &[(f64, f64)] = &(app_state.canvas_data.network_data_tx);
let x_axis: Axis<String> = Axis::default().style(Style::default().fg(GRAPH_COLOUR)).bounds([0.0, 600_000.0]); let x_axis: Axis<String> = Axis::default().style(Style::default().fg(GRAPH_COLOUR)).bounds([0.0, 600_000.0]);
let y_axis = Axis::default() let y_axis = Axis::default()
.style(Style::default().fg(GRAPH_COLOUR)) .style(Style::default().fg(GRAPH_COLOUR))
@ -441,10 +464,12 @@ fn draw_network_graph<B: backend::Backend>(
.render(f, draw_loc); .render(f, draw_loc);
} }
fn draw_network_labels<B: backend::Backend>( fn draw_network_labels<B: backend::Backend>(f: &mut Frame<B>, app_state: &mut app::App, draw_loc: Rect) {
f: &mut Frame<B>, app_state: &mut app::App, rx_display: String, tx_display: String, total_rx_display: String, total_tx_display: String, let rx_display: String = app_state.canvas_data.rx_display.clone();
draw_loc: Rect, let tx_display: String = app_state.canvas_data.tx_display.clone();
) { let total_rx_display: String = app_state.canvas_data.total_rx_display.clone();
let total_tx_display: String = app_state.canvas_data.total_tx_display.clone();
// Gross but I need it to work... // Gross but I need it to work...
let total_network = vec![vec![rx_display, tx_display, total_rx_display, total_tx_display]]; let total_network = vec![vec![rx_display, tx_display, total_rx_display, total_tx_display]];
let mapped_network = total_network.iter().map(|val| Row::Data(val.iter())); let mapped_network = total_network.iter().map(|val| Row::Data(val.iter()));
@ -468,7 +493,9 @@ fn draw_network_labels<B: backend::Backend>(
.render(f, draw_loc); .render(f, draw_loc);
} }
fn draw_temp_table<B: backend::Backend>(f: &mut Frame<B>, app_state: &mut app::App, temp_sensor_data: &[Vec<String>], draw_loc: Rect) { fn draw_temp_table<B: backend::Backend>(f: &mut Frame<B>, app_state: &mut app::App, draw_loc: Rect) {
let temp_sensor_data: &[Vec<String>] = &(app_state.canvas_data.temp_sensor_data);
let num_rows = i64::from(draw_loc.height) - 4; let num_rows = i64::from(draw_loc.height) - 4;
let start_position = get_start_position( let start_position = get_start_position(
num_rows, num_rows,
@ -511,7 +538,8 @@ fn draw_temp_table<B: backend::Backend>(f: &mut Frame<B>, app_state: &mut app::A
.render(f, draw_loc); .render(f, draw_loc);
} }
fn draw_disk_table<B: backend::Backend>(f: &mut Frame<B>, app_state: &mut app::App, disk_data: &[Vec<String>], draw_loc: Rect) { fn draw_disk_table<B: backend::Backend>(f: &mut Frame<B>, app_state: &mut app::App, draw_loc: Rect) {
let disk_data: &[Vec<String>] = &(app_state.canvas_data.disk_data);
let num_rows = i64::from(draw_loc.height) - 4; let num_rows = i64::from(draw_loc.height) - 4;
let start_position = get_start_position( let start_position = get_start_position(
num_rows, num_rows,
@ -563,7 +591,8 @@ fn draw_disk_table<B: backend::Backend>(f: &mut Frame<B>, app_state: &mut app::A
.render(f, draw_loc); .render(f, draw_loc);
} }
fn draw_processes_table<B: backend::Backend>(f: &mut Frame<B>, app_state: &mut app::App, process_data: &[Vec<String>], draw_loc: Rect) { fn draw_processes_table<B: backend::Backend>(f: &mut Frame<B>, app_state: &mut app::App, draw_loc: Rect) {
let process_data: &[ConvertedProcessData] = &(app_state.canvas_data.process_data);
let width = f64::from(draw_loc.width); let width = f64::from(draw_loc.width);
// Admittedly this is kinda a hack... but we need to: // Admittedly this is kinda a hack... but we need to:
@ -580,12 +609,18 @@ fn draw_processes_table<B: backend::Backend>(f: &mut Frame<B>, app_state: &mut a
&mut app_state.currently_selected_process_position, &mut app_state.currently_selected_process_position,
); );
let sliced_vec: Vec<Vec<String>> = (&process_data[start_position as usize..]).to_vec(); let sliced_vec: Vec<ConvertedProcessData> = (&process_data[start_position as usize..]).to_vec();
let mut process_counter = 0; let mut process_counter = 0;
let process_rows = sliced_vec.iter().map(|process| { let process_rows = sliced_vec.iter().map(|process| {
let stringified_process_vec: Vec<String> = vec![
process.pid.to_string(),
process.name.clone(),
process.cpu_usage.clone(),
process.mem_usage.clone(),
];
Row::StyledData( Row::StyledData(
process.iter(), stringified_process_vec.into_iter(),
if process_counter == app_state.currently_selected_process_position - start_position { if process_counter == app_state.currently_selected_process_position - start_position {
process_counter = -1; process_counter = -1;
Style::default().fg(Color::Black).bg(Color::Cyan) Style::default().fg(Color::Black).bg(Color::Cyan)

View File

@ -5,6 +5,7 @@ use crate::{
}; };
use constants::*; use constants::*;
#[derive(Default, Debug)]
pub struct ConvertedNetworkData { pub struct ConvertedNetworkData {
pub rx: Vec<(f64, f64)>, pub rx: Vec<(f64, f64)>,
pub tx: Vec<(f64, f64)>, pub tx: Vec<(f64, f64)>,
@ -14,6 +15,14 @@ pub struct ConvertedNetworkData {
pub total_tx_display: String, pub total_tx_display: String,
} }
#[derive(Clone, Default, Debug)]
pub struct ConvertedProcessData {
pub pid: u32,
pub name: String,
pub cpu_usage: String,
pub mem_usage: String,
}
pub fn update_temp_row(app_data: &data_collection::Data, temp_type: &data_collection::temperature::TemperatureType) -> Vec<Vec<String>> { pub fn update_temp_row(app_data: &data_collection::Data, temp_type: &data_collection::temperature::TemperatureType) -> Vec<Vec<String>> {
let mut sensor_vector: Vec<Vec<String>> = Vec::new(); let mut sensor_vector: Vec<Vec<String>> = Vec::new();
@ -84,15 +93,15 @@ pub fn update_disk_row(app_data: &data_collection::Data) -> Vec<Vec<String>> {
disk_vector disk_vector
} }
pub fn update_process_row(app_data: &data_collection::Data) -> Vec<Vec<String>> { pub fn update_process_row(app_data: &data_collection::Data) -> Vec<ConvertedProcessData> {
let mut process_vector: Vec<Vec<String>> = Vec::new(); let mut process_vector: Vec<ConvertedProcessData> = Vec::new();
for process in &app_data.list_of_processes { for process in &app_data.list_of_processes {
process_vector.push(vec![ process_vector.push(ConvertedProcessData {
process.pid.to_string(), pid: process.pid,
process.command.to_string(), name: process.command.to_string(),
format!("{:.1}%", process.cpu_usage_percent), cpu_usage: format!("{:.1}%", process.cpu_usage_percent),
format!( mem_usage: format!(
"{:.1}%", "{:.1}%",
if let Some(mem_usage) = process.mem_usage_percent { if let Some(mem_usage) = process.mem_usage_percent {
mem_usage mem_usage
@ -106,7 +115,7 @@ pub fn update_process_row(app_data: &data_collection::Data) -> Vec<Vec<String>>
0_f64 0_f64
} }
), ),
]); });
} }
process_vector process_vector
@ -300,7 +309,11 @@ pub fn convert_network_data_points(network_data: &[data_collection::network::Net
total_rx_converted_result = get_exact_byte_values(0, false); total_rx_converted_result = get_exact_byte_values(0, false);
} }
let rx_display = format!("{:.*}{}", 1, rx_converted_result.0, rx_converted_result.1); let rx_display = format!("{:.*}{}", 1, rx_converted_result.0, rx_converted_result.1);
let total_rx_display = format!("{:.*}{}", 1, total_rx_converted_result.0, total_rx_converted_result.1); let total_rx_display = if cfg!(not(target_os = "windows")) {
format!("{:.*}{}", 1, total_rx_converted_result.0, total_rx_converted_result.1)
} else {
"N/A".to_string()
};
if let Some(last_num_bytes_entry) = network_data.last() { if let Some(last_num_bytes_entry) = network_data.last() {
tx_converted_result = get_exact_byte_values(last_num_bytes_entry.tx, false); tx_converted_result = get_exact_byte_values(last_num_bytes_entry.tx, false);
@ -310,7 +323,11 @@ pub fn convert_network_data_points(network_data: &[data_collection::network::Net
total_tx_converted_result = get_exact_byte_values(0, false); total_tx_converted_result = get_exact_byte_values(0, false);
} }
let tx_display = format!("{:.*}{}", 1, tx_converted_result.0, tx_converted_result.1); let tx_display = format!("{:.*}{}", 1, tx_converted_result.0, tx_converted_result.1);
let total_tx_display = format!("{:.*}{}", 1, total_tx_converted_result.0, total_tx_converted_result.1); let total_tx_display = if cfg!(not(target_os = "windows")) {
format!("{:.*}{}", 1, total_tx_converted_result.0, total_tx_converted_result.1)
} else {
"N/A".to_string()
};
ConvertedNetworkData { ConvertedNetworkData {
rx, rx,

View File

@ -177,7 +177,7 @@ fn main() -> error::Result<()> {
if let Ok(message) = rrx.try_recv() { if let Ok(message) = rrx.try_recv() {
match message { match message {
ResetEvent::Reset => { ResetEvent::Reset => {
debug!("Received reset message"); //debug!("Received reset message");
first_run = true; first_run = true;
data_state.data = app::data_collection::Data::default(); data_state.data = app::data_collection::Data::default();
} }
@ -197,7 +197,6 @@ fn main() -> error::Result<()> {
}); });
} }
let mut canvas_data = canvas::CanvasData::default();
loop { loop {
if let Ok(recv) = rx.recv_timeout(Duration::from_millis(TICK_RATE_IN_MILLISECONDS)) { if let Ok(recv) = rx.recv_timeout(Duration::from_millis(TICK_RATE_IN_MILLISECONDS)) {
match recv { match recv {
@ -213,8 +212,9 @@ fn main() -> error::Result<()> {
KeyCode::Char('j') => app.on_down(), KeyCode::Char('j') => app.on_down(),
KeyCode::Up => app.decrement_position_count(), KeyCode::Up => app.decrement_position_count(),
KeyCode::Down => app.increment_position_count(), KeyCode::Down => app.increment_position_count(),
KeyCode::Char(uncaught_char) => app.on_key(uncaught_char), KeyCode::Char(uncaught_char) => app.on_char_key(uncaught_char),
KeyCode::Esc => app.on_esc(), KeyCode::Esc => app.reset(),
KeyCode::Enter => app.on_enter(),
_ => {} _ => {}
} }
} else { } else {
@ -260,7 +260,7 @@ fn main() -> error::Result<()> {
&app.process_sorting_type, &app.process_sorting_type,
app.process_sorting_reverse, app.process_sorting_reverse,
); );
canvas_data.process_data = update_process_row(&app.data); app.canvas_data.process_data = update_process_row(&app.data);
app.to_be_resorted = false; app.to_be_resorted = false;
} }
} }
@ -270,6 +270,8 @@ fn main() -> error::Result<()> {
_ => {} _ => {}
}, },
Event::Update(data) => { Event::Update(data) => {
// NOTE TO SELF - data is refreshed into app state HERE! That means, if it is
// frozen, then, app.data is never refreshed, until unfrozen!
if !app.is_frozen { if !app.is_frozen {
app.data = *data; app.data = *data;
@ -280,28 +282,28 @@ fn main() -> error::Result<()> {
); );
// Convert all data into tui components // Convert all data into tui components
// TODO: Note that we might want to move this the canvas' side... consider.
let network_data = update_network_data_points(&app.data); let network_data = update_network_data_points(&app.data);
canvas_data.network_data_rx = network_data.rx; app.canvas_data.network_data_rx = network_data.rx;
canvas_data.network_data_tx = network_data.tx; app.canvas_data.network_data_tx = network_data.tx;
canvas_data.rx_display = network_data.rx_display; app.canvas_data.rx_display = network_data.rx_display;
canvas_data.tx_display = network_data.tx_display; app.canvas_data.tx_display = network_data.tx_display;
canvas_data.total_rx_display = network_data.total_rx_display; app.canvas_data.total_rx_display = network_data.total_rx_display;
canvas_data.total_tx_display = network_data.total_tx_display; app.canvas_data.total_tx_display = network_data.total_tx_display;
canvas_data.disk_data = update_disk_row(&app.data); app.canvas_data.disk_data = update_disk_row(&app.data);
canvas_data.temp_sensor_data = update_temp_row(&app.data, &app.temperature_type); app.canvas_data.temp_sensor_data = update_temp_row(&app.data, &app.temperature_type);
canvas_data.process_data = update_process_row(&app.data); app.canvas_data.process_data = update_process_row(&app.data);
canvas_data.mem_data = update_mem_data_points(&app.data); app.canvas_data.mem_data = update_mem_data_points(&app.data);
canvas_data.memory_labels = update_mem_data_values(&app.data); app.canvas_data.memory_labels = update_mem_data_values(&app.data);
canvas_data.swap_data = update_swap_data_points(&app.data); app.canvas_data.swap_data = update_swap_data_points(&app.data);
canvas_data.cpu_data = update_cpu_data_points(app.show_average_cpu, &app.data); app.canvas_data.cpu_data = update_cpu_data_points(app.show_average_cpu, &app.data);
//debug!("Update event complete."); //debug!("Update event complete.");
} }
} }
} }
} }
// Draw! // Draw!
if let Err(err) = canvas::draw_data(&mut terminal, &mut app, &canvas_data) { if let Err(err) = canvas::draw_data(&mut terminal, &mut app) {
disable_raw_mode()?; disable_raw_mode()?;
execute!(terminal.backend_mut(), LeaveAlternateScreen)?; execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
terminal.show_cursor()?; terminal.show_cursor()?;