2
0
Fork 0
mirror of https://github.com/ClementTsang/bottom synced 2025-02-15 12:48:28 +00:00

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 std::time::Instant;
use crate::constants;
use crate::{canvas, constants, data_conversion::ConvertedProcessData, utils::error::Result};
mod process_killer;
@ -47,10 +47,14 @@ pub struct App {
second_char: char,
pub use_dot: bool,
pub show_help: bool,
pub show_dd: bool,
pub dd_err: Option<String>,
to_delete_process: Option<ConvertedProcessData>,
pub is_frozen: bool,
pub left_legend: bool,
pub use_current_cpu_total: bool,
last_key_press: Instant,
pub canvas_data: canvas::CanvasData,
}
impl App {
@ -80,15 +84,23 @@ impl App {
second_char: ' ',
use_dot,
show_help: false,
show_dd: false,
dd_err: None,
to_delete_process: None,
is_frozen: false,
left_legend,
use_current_cpu_total,
last_key_press: Instant::now(),
canvas_data: canvas::CanvasData::default(),
}
}
pub fn reset(&mut self) {
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) {
@ -96,18 +108,30 @@ impl App {
self.second_char = ' ';
}
pub fn on_enter(&mut self) {}
pub fn on_esc(&mut self) {
if self.show_help {
self.show_help = false;
}
self.reset_multi_tap_keys();
fn is_in_dialog(&self) -> bool {
self.show_help || self.show_dd
}
// TODO: How should we make it for process panel specific hotkeys? Only if we're on process panel? Or what?
pub fn on_key(&mut self, caught_char: char) {
if !self.show_help {
/// One of two functions allowed to run while in a dialog...
pub fn on_enter(&mut self) {
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();
if current_key_press_inst.duration_since(self.last_key_press).as_millis() > constants::MAX_KEY_TIMEOUT_IN_MILLISECONDS {
self.reset_multi_tap_keys();
@ -120,7 +144,11 @@ impl App {
if self.awaiting_second_char && self.second_char == 'd' {
self.awaiting_second_char = false;
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 {
self.awaiting_second_char = true;
self.second_char = 'd';
@ -196,12 +224,21 @@ impl App {
}
}
fn kill_highlighted_process(&self) -> crate::utils::error::Result<()> {
let current_pid = u64::from(self.data.list_of_processes[self.currently_selected_process_position as usize].pid);
process_killer::kill_process_given_pid(current_pid)?;
pub fn kill_highlighted_process(&mut self) -> Result<()> {
// Technically unnecessary but this is a good check...
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(())
}
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!
//
// General idea for now:
@ -212,91 +249,118 @@ impl App {
// Network -(up)> MEM, -(right)> PROC
// PROC -(up)> Disk, -(left)> Network
pub fn on_left(&mut self) {
self.current_application_position = match self.current_application_position {
ApplicationPosition::Process => ApplicationPosition::Network,
ApplicationPosition::Disk => ApplicationPosition::Mem,
ApplicationPosition::Temp => ApplicationPosition::Mem,
_ => self.current_application_position,
};
self.reset_multi_tap_keys();
if !self.is_in_dialog() {
self.current_application_position = match self.current_application_position {
ApplicationPosition::Process => ApplicationPosition::Network,
ApplicationPosition::Disk => ApplicationPosition::Mem,
ApplicationPosition::Temp => ApplicationPosition::Mem,
_ => self.current_application_position,
};
self.reset_multi_tap_keys();
}
}
pub fn on_right(&mut self) {
self.current_application_position = match self.current_application_position {
ApplicationPosition::Mem => ApplicationPosition::Temp,
ApplicationPosition::Network => ApplicationPosition::Process,
_ => self.current_application_position,
};
self.reset_multi_tap_keys();
if !self.is_in_dialog() {
self.current_application_position = match self.current_application_position {
ApplicationPosition::Mem => ApplicationPosition::Temp,
ApplicationPosition::Network => ApplicationPosition::Process,
_ => self.current_application_position,
};
self.reset_multi_tap_keys();
}
}
pub fn on_up(&mut self) {
self.current_application_position = match self.current_application_position {
ApplicationPosition::Mem => ApplicationPosition::Cpu,
ApplicationPosition::Network => ApplicationPosition::Mem,
ApplicationPosition::Process => ApplicationPosition::Disk,
ApplicationPosition::Temp => ApplicationPosition::Cpu,
ApplicationPosition::Disk => ApplicationPosition::Temp,
_ => self.current_application_position,
};
self.reset_multi_tap_keys();
if !self.is_in_dialog() {
self.current_application_position = match self.current_application_position {
ApplicationPosition::Mem => ApplicationPosition::Cpu,
ApplicationPosition::Network => ApplicationPosition::Mem,
ApplicationPosition::Process => ApplicationPosition::Disk,
ApplicationPosition::Temp => ApplicationPosition::Cpu,
ApplicationPosition::Disk => ApplicationPosition::Temp,
_ => self.current_application_position,
};
self.reset_multi_tap_keys();
}
}
pub fn on_down(&mut self) {
self.current_application_position = match self.current_application_position {
ApplicationPosition::Cpu => ApplicationPosition::Mem,
ApplicationPosition::Mem => ApplicationPosition::Network,
ApplicationPosition::Temp => ApplicationPosition::Disk,
ApplicationPosition::Disk => ApplicationPosition::Process,
_ => self.current_application_position,
};
self.reset_multi_tap_keys();
if !self.is_in_dialog() {
self.current_application_position = match self.current_application_position {
ApplicationPosition::Cpu => ApplicationPosition::Mem,
ApplicationPosition::Mem => ApplicationPosition::Network,
ApplicationPosition::Temp => ApplicationPosition::Disk,
ApplicationPosition::Disk => ApplicationPosition::Process,
_ => self.current_application_position,
};
self.reset_multi_tap_keys();
}
}
pub fn skip_to_first(&mut self) {
match self.current_application_position {
ApplicationPosition::Process => self.currently_selected_process_position = 0,
ApplicationPosition::Temp => self.currently_selected_temperature_position = 0,
ApplicationPosition::Disk => self.currently_selected_disk_position = 0,
_ => {}
if !self.is_in_dialog() {
match self.current_application_position {
ApplicationPosition::Process => self.currently_selected_process_position = 0,
ApplicationPosition::Temp => self.currently_selected_temperature_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.reset_multi_tap_keys();
}
self.scroll_direction = ScrollDirection::UP;
self.reset_multi_tap_keys();
}
pub fn skip_to_last(&mut self) {
match self.current_application_position {
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::Disk => self.currently_selected_disk_position = self.data.list_of_disks.len() as i64 - 1,
_ => {}
if !self.is_in_dialog() {
match self.current_application_position {
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::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.reset_multi_tap_keys();
}
self.scroll_direction = ScrollDirection::DOWN;
self.reset_multi_tap_keys();
}
pub fn decrement_position_count(&mut self) {
match self.current_application_position {
ApplicationPosition::Process => self.change_process_position(-1),
ApplicationPosition::Temp => self.change_temp_position(-1),
ApplicationPosition::Disk => self.change_disk_position(-1),
ApplicationPosition::Cpu => self.change_cpu_table_position(-1), // TODO: Temporary, may change if we add scaling
_ => {}
if !self.is_in_dialog() {
match self.current_application_position {
ApplicationPosition::Process => self.change_process_position(-1),
ApplicationPosition::Temp => self.change_temp_position(-1),
ApplicationPosition::Disk => self.change_disk_position(-1),
ApplicationPosition::Cpu => self.change_cpu_table_position(-1), // TODO: Temporary, may change if we add scaling
_ => {}
}
self.scroll_direction = ScrollDirection::UP;
self.reset_multi_tap_keys();
}
self.scroll_direction = ScrollDirection::UP;
self.reset_multi_tap_keys();
}
pub fn increment_position_count(&mut self) {
match self.current_application_position {
ApplicationPosition::Process => self.change_process_position(1),
ApplicationPosition::Temp => self.change_temp_position(1),
ApplicationPosition::Disk => self.change_disk_position(1),
ApplicationPosition::Cpu => self.change_cpu_table_position(1), // TODO: Temporary, may change if we add scaling
_ => {}
if !self.is_in_dialog() {
match self.current_application_position {
ApplicationPosition::Process => self.change_process_position(1),
ApplicationPosition::Temp => self.change_temp_position(1),
ApplicationPosition::Disk => self.change_disk_position(1),
ApplicationPosition::Cpu => self.change_cpu_table_position(1), // TODO: Temporary, may change if we add scaling
_ => {}
}
self.scroll_direction = ScrollDirection::DOWN;
self.reset_multi_tap_keys();
}
self.scroll_direction = ScrollDirection::DOWN;
self.reset_multi_tap_keys();
}
fn change_cpu_table_position(&mut self, num_to_change_by: i64) {

View file

@ -1,8 +1,7 @@
/// This file is meant to house (OS specific) implementations on how to kill processes.
use crate::utils::error::{BottomError, Result};
use std::process::Command;
// TODO: Make it update process list on freeze.
// Copied from SO: https://stackoverflow.com/a/55231715
#[cfg(target_os = "windows")]
use winapi::{
@ -33,7 +32,7 @@ impl Process {
}
/// 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") {
// Linux
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") {
// TODO: macOS
// 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 {
// 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(())

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::{
backend,
layout::{Alignment, Constraint, Direction, Layout, Rect},
@ -46,7 +46,7 @@ pub struct CanvasData {
pub network_data_tx: Vec<(f64, f64)>,
pub disk_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 mem_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
}
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.draw(|mut f| {
if app_state.show_help {
// Only for the dialog (help, dd) menus
// Only for the help
let vertical_dialog_chunk = Layout::default()
.direction(Direction::Vertical)
.margin(1)
@ -116,6 +116,52 @@ pub fn draw_data<B: backend::Backend>(terminal: &mut Terminal<B>, app_state: &mu
.alignment(Alignment::Left)
.wrap(true)
.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 {
let vertical_chunks = Layout::default()
.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
// 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
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
draw_memory_graph(
&mut f,
&app_state,
&canvas_data.memory_labels,
&canvas_data.mem_data,
&canvas_data.swap_data,
middle_chunks[0],
);
draw_memory_graph(&mut f, &app_state, middle_chunks[0]);
// Network graph
draw_network_graph(
&mut f,
&app_state,
&canvas_data.network_data_rx,
&canvas_data.network_data_tx,
if cfg!(not(target_os = "windows")) {
network_chunk[0]
} 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,
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],
);
}
draw_network_labels(&mut f, app_state, network_chunk[1]);
// 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
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
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(())
}
fn draw_cpu_graph<B: backend::Backend>(f: &mut Frame<B>, app_state: &app::App, cpu_data: &[(String, Vec<(f64, f64)>)], draw_loc: Rect) {
// CPU usage graph
fn draw_cpu_graph<B: backend::Backend>(f: &mut Frame<B>, app_state: &app::App, draw_loc: Rect) {
let cpu_data: &[(String, Vec<(f64, f64)>)] = &app_state.canvas_data.cpu_data;
// CPU usage graph
let x_axis: Axis<String> = Axis::default()
.style(Style::default().fg(GRAPH_COLOUR))
.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);
}
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 start_position = get_start_position(
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);
}
fn draw_memory_graph<B: backend::Backend>(
f: &mut Frame<B>, app_state: &app::App, memory_labels: &[(u64, u64)], mem_data: &[(f64, f64)], swap_data: &[(f64, f64)], draw_loc: Rect,
) {
fn draw_memory_graph<B: backend::Backend>(f: &mut Frame<B>, app_state: &app::App, 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()
.style(Style::default().fg(GRAPH_COLOUR))
.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);
}
fn draw_network_graph<B: backend::Backend>(
f: &mut Frame<B>, app_state: &app::App, network_data_rx: &[(f64, f64)], network_data_tx: &[(f64, f64)], draw_loc: Rect,
) {
fn draw_network_graph<B: backend::Backend>(f: &mut Frame<B>, app_state: &app::App, 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 y_axis = Axis::default()
.style(Style::default().fg(GRAPH_COLOUR))
@ -441,10 +464,12 @@ fn draw_network_graph<B: backend::Backend>(
.render(f, draw_loc);
}
fn draw_network_labels<B: backend::Backend>(
f: &mut Frame<B>, app_state: &mut app::App, rx_display: String, tx_display: String, total_rx_display: String, total_tx_display: String,
draw_loc: Rect,
) {
fn draw_network_labels<B: backend::Backend>(f: &mut Frame<B>, app_state: &mut app::App, draw_loc: Rect) {
let rx_display: String = app_state.canvas_data.rx_display.clone();
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...
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()));
@ -468,7 +493,9 @@ fn draw_network_labels<B: backend::Backend>(
.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 start_position = get_start_position(
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);
}
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 start_position = get_start_position(
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);
}
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);
// 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,
);
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 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(
process.iter(),
stringified_process_vec.into_iter(),
if process_counter == app_state.currently_selected_process_position - start_position {
process_counter = -1;
Style::default().fg(Color::Black).bg(Color::Cyan)

View file

@ -5,6 +5,7 @@ use crate::{
};
use constants::*;
#[derive(Default, Debug)]
pub struct ConvertedNetworkData {
pub rx: Vec<(f64, f64)>,
pub tx: Vec<(f64, f64)>,
@ -14,6 +15,14 @@ pub struct ConvertedNetworkData {
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>> {
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
}
pub fn update_process_row(app_data: &data_collection::Data) -> Vec<Vec<String>> {
let mut process_vector: Vec<Vec<String>> = Vec::new();
pub fn update_process_row(app_data: &data_collection::Data) -> Vec<ConvertedProcessData> {
let mut process_vector: Vec<ConvertedProcessData> = Vec::new();
for process in &app_data.list_of_processes {
process_vector.push(vec![
process.pid.to_string(),
process.command.to_string(),
format!("{:.1}%", process.cpu_usage_percent),
format!(
process_vector.push(ConvertedProcessData {
pid: process.pid,
name: process.command.to_string(),
cpu_usage: format!("{:.1}%", process.cpu_usage_percent),
mem_usage: format!(
"{:.1}%",
if let Some(mem_usage) = process.mem_usage_percent {
mem_usage
@ -106,7 +115,7 @@ pub fn update_process_row(app_data: &data_collection::Data) -> Vec<Vec<String>>
0_f64
}
),
]);
});
}
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);
}
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() {
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);
}
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 {
rx,

View file

@ -177,7 +177,7 @@ fn main() -> error::Result<()> {
if let Ok(message) = rrx.try_recv() {
match message {
ResetEvent::Reset => {
debug!("Received reset message");
//debug!("Received reset message");
first_run = true;
data_state.data = app::data_collection::Data::default();
}
@ -197,7 +197,6 @@ fn main() -> error::Result<()> {
});
}
let mut canvas_data = canvas::CanvasData::default();
loop {
if let Ok(recv) = rx.recv_timeout(Duration::from_millis(TICK_RATE_IN_MILLISECONDS)) {
match recv {
@ -213,8 +212,9 @@ fn main() -> error::Result<()> {
KeyCode::Char('j') => app.on_down(),
KeyCode::Up => app.decrement_position_count(),
KeyCode::Down => app.increment_position_count(),
KeyCode::Char(uncaught_char) => app.on_key(uncaught_char),
KeyCode::Esc => app.on_esc(),
KeyCode::Char(uncaught_char) => app.on_char_key(uncaught_char),
KeyCode::Esc => app.reset(),
KeyCode::Enter => app.on_enter(),
_ => {}
}
} else {
@ -260,7 +260,7 @@ fn main() -> error::Result<()> {
&app.process_sorting_type,
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;
}
}
@ -270,6 +270,8 @@ fn main() -> error::Result<()> {
_ => {}
},
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 {
app.data = *data;
@ -280,28 +282,28 @@ fn main() -> error::Result<()> {
);
// 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);
canvas_data.network_data_rx = network_data.rx;
canvas_data.network_data_tx = network_data.tx;
canvas_data.rx_display = network_data.rx_display;
canvas_data.tx_display = network_data.tx_display;
canvas_data.total_rx_display = network_data.total_rx_display;
canvas_data.total_tx_display = network_data.total_tx_display;
canvas_data.disk_data = update_disk_row(&app.data);
canvas_data.temp_sensor_data = update_temp_row(&app.data, &app.temperature_type);
canvas_data.process_data = update_process_row(&app.data);
canvas_data.mem_data = update_mem_data_points(&app.data);
canvas_data.memory_labels = update_mem_data_values(&app.data);
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.network_data_rx = network_data.rx;
app.canvas_data.network_data_tx = network_data.tx;
app.canvas_data.rx_display = network_data.rx_display;
app.canvas_data.tx_display = network_data.tx_display;
app.canvas_data.total_rx_display = network_data.total_rx_display;
app.canvas_data.total_tx_display = network_data.total_tx_display;
app.canvas_data.disk_data = update_disk_row(&app.data);
app.canvas_data.temp_sensor_data = update_temp_row(&app.data, &app.temperature_type);
app.canvas_data.process_data = update_process_row(&app.data);
app.canvas_data.mem_data = update_mem_data_points(&app.data);
app.canvas_data.memory_labels = update_mem_data_values(&app.data);
app.canvas_data.swap_data = update_swap_data_points(&app.data);
app.canvas_data.cpu_data = update_cpu_data_points(app.show_average_cpu, &app.data);
//debug!("Update event complete.");
}
}
}
}
// 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()?;
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
terminal.show_cursor()?;