mirror of
https://github.com/ClementTsang/bottom
synced 2024-11-22 12:13:06 +00:00
Added clap support for command line options, as well as tweaked some table placement.
This commit is contained in:
parent
3219abeaa5
commit
6d9ed34dcb
5 changed files with 91 additions and 59 deletions
|
@ -21,6 +21,7 @@ Currently, I'm unable to test on MacOS, so I'm not sure how well this will work,
|
|||
* As mentioned, this project is very much inspired by both [gotop](https://github.com/cjbassi/gotop) and [gtop](https://github.com/aksakalli/gtop) .
|
||||
|
||||
* This application was written with the following libraries:
|
||||
* clap
|
||||
* [crossterm](https://github.com/TimonPost/crossterm)
|
||||
* [heim](https://github.com/heim-rs/heim)
|
||||
* [sysinfo](https://github.com/GuillaumeGomez/sysinfo)
|
||||
|
|
30
src/app.rs
30
src/app.rs
|
@ -5,22 +5,14 @@ use std::collections::HashMap;
|
|||
use sysinfo::{System, SystemExt};
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone)]
|
||||
pub enum TemperatureType {
|
||||
Celsius,
|
||||
Kelvin,
|
||||
Fahrenheit,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct App<'a> {
|
||||
title : &'a str,
|
||||
pub struct App {
|
||||
pub should_quit : bool,
|
||||
pub process_sorting_type : processes::ProcessSorting,
|
||||
pub process_sorting_reverse : bool,
|
||||
pub to_be_resorted : bool,
|
||||
pub current_selected_process_position : u64,
|
||||
pub temperature_type : TemperatureType,
|
||||
pub temperature_type : data_collection::temperature::TemperatureType,
|
||||
pub update_rate_in_milliseconds : u64,
|
||||
}
|
||||
|
||||
fn set_if_valid<T : std::clone::Clone>(result : &Result<T, heim::Error>, value_to_set : &mut T) {
|
||||
|
@ -56,7 +48,7 @@ pub struct DataState {
|
|||
prev_pid_stats : HashMap<String, f64>, // TODO: Purge list?
|
||||
prev_idle : f64,
|
||||
prev_non_idle : f64,
|
||||
temperature_type : TemperatureType,
|
||||
temperature_type : data_collection::temperature::TemperatureType,
|
||||
}
|
||||
|
||||
impl Default for DataState {
|
||||
|
@ -69,7 +61,7 @@ impl Default for DataState {
|
|||
prev_pid_stats : HashMap::new(),
|
||||
prev_idle : 0_f64,
|
||||
prev_non_idle : 0_f64,
|
||||
temperature_type : TemperatureType::Celsius,
|
||||
temperature_type : data_collection::temperature::TemperatureType::Celsius,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +71,7 @@ impl DataState {
|
|||
self.stale_max_seconds = stale_max_seconds;
|
||||
}
|
||||
|
||||
pub fn set_temperature_type(&mut self, temperature_type : TemperatureType) {
|
||||
pub fn set_temperature_type(&mut self, temperature_type : data_collection::temperature::TemperatureType) {
|
||||
self.temperature_type = temperature_type;
|
||||
}
|
||||
|
||||
|
@ -108,7 +100,7 @@ impl DataState {
|
|||
set_if_valid(&disks::get_disk_usage_list().await, &mut self.data.list_of_disks);
|
||||
push_if_valid(&disks::get_io_usage_list(false).await, &mut self.data.list_of_io);
|
||||
push_if_valid(&disks::get_io_usage_list(true).await, &mut self.data.list_of_physical_io);
|
||||
set_if_valid(&temperature::get_temperature_data().await, &mut self.data.list_of_temperature_sensor);
|
||||
set_if_valid(&temperature::get_temperature_data(&self.temperature_type).await, &mut self.data.list_of_temperature_sensor);
|
||||
|
||||
if self.first_run {
|
||||
self.data = Data::default();
|
||||
|
@ -170,16 +162,16 @@ impl DataState {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> App<'a> {
|
||||
pub fn new(title : &str) -> App {
|
||||
impl App {
|
||||
pub fn new(temperature_type : data_collection::temperature::TemperatureType, update_rate_in_milliseconds : u64) -> App {
|
||||
App {
|
||||
title,
|
||||
process_sorting_type : processes::ProcessSorting::CPU, // TODO: Change this based on input args... basically set this on app creation
|
||||
should_quit : false,
|
||||
process_sorting_reverse : true,
|
||||
to_be_resorted : false,
|
||||
current_selected_process_position : 0,
|
||||
temperature_type : TemperatureType::Celsius,
|
||||
temperature_type,
|
||||
update_rate_in_milliseconds,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,14 @@ pub struct TempData {
|
|||
pub temperature : f32,
|
||||
}
|
||||
|
||||
pub async fn get_temperature_data() -> Result<Vec<TempData>, heim::Error> {
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum TemperatureType {
|
||||
Celsius,
|
||||
Kelvin,
|
||||
Fahrenheit,
|
||||
}
|
||||
|
||||
pub async fn get_temperature_data(temp_type : &TemperatureType) -> Result<Vec<TempData>, heim::Error> {
|
||||
let mut temperature_vec : Vec<TempData> = Vec::new();
|
||||
|
||||
let mut sensor_data = heim::sensors::temperatures();
|
||||
|
@ -14,7 +21,11 @@ pub async fn get_temperature_data() -> Result<Vec<TempData>, heim::Error> {
|
|||
if let Ok(sensor) = sensor {
|
||||
temperature_vec.push(TempData {
|
||||
component_name : Box::from(sensor.unit()),
|
||||
temperature : sensor.current().get::<thermodynamic_temperature::degree_celsius>(), // TODO: Allow for toggling this!
|
||||
temperature : match temp_type {
|
||||
TemperatureType::Celsius => sensor.current().get::<thermodynamic_temperature::degree_celsius>(),
|
||||
TemperatureType::Kelvin => sensor.current().get::<thermodynamic_temperature::kelvin>(),
|
||||
TemperatureType::Fahrenheit => sensor.current().get::<thermodynamic_temperature::degree_fahrenheit>(),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,11 +74,6 @@ pub fn draw_data<B : tui::backend::Backend>(terminal : &mut Terminal<B>, canvas_
|
|||
.margin(0)
|
||||
.constraints([Constraint::Percentage(40), Constraint::Percentage(60)].as_ref())
|
||||
.split(bottom_divided_chunk_1[0]);
|
||||
let bottom_divided_chunk_1_2 = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.margin(0)
|
||||
.constraints([Constraint::Percentage(40), Constraint::Percentage(60)].as_ref())
|
||||
.split(bottom_divided_chunk_1[1]);
|
||||
|
||||
// Set up blocks and their components
|
||||
|
||||
|
@ -133,18 +128,14 @@ pub fn draw_data<B : tui::backend::Backend>(terminal : &mut Terminal<B>, canvas_
|
|||
Block::default().title("Network").borders(Borders::ALL).border_style(border_style).render(&mut f, middle_chunks[1]);
|
||||
|
||||
// Temperature table
|
||||
Table::new(["Sensor", "Temperature"].iter(), temperature_rows)
|
||||
.block(Block::default().title("Temperatures").borders(Borders::ALL).border_style(border_style))
|
||||
.header_style(Style::default().fg(Color::LightBlue))
|
||||
.widths(&[15, 5])
|
||||
.render(&mut f, bottom_divided_chunk_1_1[0]);
|
||||
|
||||
// Disk usage table
|
||||
Table::new(["Disk", "Mount", "Used", "Total", "Free"].iter(), disk_rows)
|
||||
.block(Block::default().title("Disk Usage").borders(Borders::ALL).border_style(border_style))
|
||||
.header_style(Style::default().fg(Color::LightBlue).modifier(Modifier::BOLD))
|
||||
.widths(&[15, 10, 5, 5, 5])
|
||||
.render(&mut f, bottom_divided_chunk_1_2[0]);
|
||||
{
|
||||
let width = f64::from(bottom_divided_chunk_1_1[0].width);
|
||||
Table::new(["Sensor", "Temp"].iter(), temperature_rows)
|
||||
.block(Block::default().title("Temperatures").borders(Borders::ALL).border_style(border_style))
|
||||
.header_style(Style::default().fg(Color::LightBlue))
|
||||
.widths(&[(width * 0.45) as u16, (width * 0.4) as u16])
|
||||
.render(&mut f, bottom_divided_chunk_1_1[0]);
|
||||
}
|
||||
|
||||
// Temp graph
|
||||
Block::default()
|
||||
|
@ -153,19 +144,25 @@ pub fn draw_data<B : tui::backend::Backend>(terminal : &mut Terminal<B>, canvas_
|
|||
.border_style(border_style)
|
||||
.render(&mut f, bottom_divided_chunk_1_1[1]);
|
||||
|
||||
// IO graph
|
||||
Block::default()
|
||||
.title("IO Usage")
|
||||
.borders(Borders::ALL)
|
||||
.border_style(border_style)
|
||||
.render(&mut f, bottom_divided_chunk_1_2[1]);
|
||||
// Disk usage table
|
||||
{
|
||||
let width = f64::from(bottom_divided_chunk_1[1].width);
|
||||
Table::new(["Disk", "Mount", "Used", "Total", "Free"].iter(), disk_rows)
|
||||
.block(Block::default().title("Disk Usage").borders(Borders::ALL).border_style(border_style))
|
||||
.header_style(Style::default().fg(Color::LightBlue).modifier(Modifier::BOLD))
|
||||
.widths(&[(width * 0.25) as u16, (width * 0.2) as u16, (width * 0.15) as u16, (width * 0.15) as u16, (width * 0.15) as u16])
|
||||
.render(&mut f, bottom_divided_chunk_1[1]);
|
||||
}
|
||||
|
||||
// Processes table
|
||||
Table::new(["PID", "Name", "CPU%", "Mem%"].iter(), process_rows)
|
||||
.block(Block::default().title("Processes").borders(Borders::ALL).border_style(border_style))
|
||||
.header_style(Style::default().fg(Color::LightBlue))
|
||||
.widths(&[5, 15, 10, 10])
|
||||
.render(&mut f, bottom_chunks[1]);
|
||||
{
|
||||
let width = f64::from(bottom_chunks[1].width);
|
||||
Table::new(["PID", "Name", "CPU%", "Mem%"].iter(), process_rows)
|
||||
.block(Block::default().title("Processes").borders(Borders::ALL).border_style(border_style))
|
||||
.header_style(Style::default().fg(Color::LightBlue))
|
||||
.widths(&[(width * 0.2) as u16, (width * 0.35) as u16, (width * 0.2) as u16, (width * 0.2) as u16])
|
||||
.render(&mut f, bottom_chunks[1]);
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
|
|
51
src/main.rs
51
src/main.rs
|
@ -16,25 +16,56 @@ mod canvas;
|
|||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
#[macro_use]
|
||||
extern crate clap;
|
||||
|
||||
enum Event<I> {
|
||||
Input(I),
|
||||
Update(Box<app::Data>),
|
||||
}
|
||||
|
||||
const STALE_MAX_MILLISECONDS : u64 = 60 * 1000;
|
||||
const TICK_RATE_IN_MILLISECONDS : u64 = 250;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), io::Error> {
|
||||
let _log = utils::logging::init_logger(); // TODO: Error handling
|
||||
|
||||
let matches = clap_app!(app =>
|
||||
(name: "rustop")
|
||||
(version: crate_version!())
|
||||
(author: "Clement Tsang <clementjhtsang@gmail.com>")
|
||||
(about: "A graphical top clone.")
|
||||
(@arg THEME: -t --theme +takes_value "Sets a colour theme.")
|
||||
(@group TEMPERATURE_TYPE =>
|
||||
(@arg celsius : -c --celsius "Sets the temperature type to Celsius. This is the default option.")
|
||||
(@arg fahrenheit : -f --fahrenheit "Sets the temperature type to Fahrenheit.")
|
||||
(@arg kelvin : -k --kelvin "Sets the temperature type to Kelvin.")
|
||||
|
||||
)
|
||||
(@arg RATE: -r --rate +takes_value "Sets a refresh rate in milliseconds, min is 250ms, defaults to 1000ms. Higher values may take more resources.")
|
||||
)
|
||||
.after_help("Themes:")
|
||||
.get_matches();
|
||||
|
||||
let screen = AlternateScreen::to_alternate(true)?;
|
||||
let backend = CrosstermBackend::with_alternate_screen(screen)?;
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
|
||||
let tick_rate_in_milliseconds : u64 = 250;
|
||||
let update_rate_in_milliseconds : u64 = 500; // TODO: Must set a check to prevent this from going into negatives!
|
||||
let update_rate_in_milliseconds : u64 = matches.value_of("rate").unwrap_or("1000").parse::<u64>().unwrap_or(1000);
|
||||
let temperature_type = if matches.is_present("fahrenheit") {
|
||||
app::data_collection::temperature::TemperatureType::Fahrenheit
|
||||
}
|
||||
else if matches.is_present("kelvin") {
|
||||
app::data_collection::temperature::TemperatureType::Kelvin
|
||||
}
|
||||
else {
|
||||
app::data_collection::temperature::TemperatureType::Celsius
|
||||
};
|
||||
|
||||
let mut app = app::App::new("rustop");
|
||||
info!("Temperature type: {:?}", temperature_type);
|
||||
|
||||
let _log = utils::logging::init_logger();
|
||||
let mut app = app::App::new(temperature_type, if update_rate_in_milliseconds < 250 { 250 } else { update_rate_in_milliseconds });
|
||||
|
||||
terminal.hide_cursor()?;
|
||||
// Setup input handling
|
||||
|
@ -77,7 +108,7 @@ async fn main() -> Result<(), io::Error> {
|
|||
let mut canvas_data = canvas::CanvasData::default();
|
||||
|
||||
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 {
|
||||
Event::Input(event) => {
|
||||
debug!("Input event fired!");
|
||||
|
@ -125,17 +156,17 @@ async fn main() -> Result<(), io::Error> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn update_temp_row(app_data : &app::Data, temp_type : &app::TemperatureType) -> Vec<Vec<String>> {
|
||||
fn update_temp_row(app_data : &app::Data, temp_type : &app::data_collection::temperature::TemperatureType) -> Vec<Vec<String>> {
|
||||
let mut sensor_vector : Vec<Vec<String>> = Vec::new();
|
||||
|
||||
for sensor in &app_data.list_of_temperature_sensor {
|
||||
sensor_vector.push(vec![
|
||||
sensor.component_name.to_string(),
|
||||
sensor.temperature.to_string()
|
||||
(sensor.temperature.ceil() as u64).to_string()
|
||||
+ match temp_type {
|
||||
app::TemperatureType::Celsius => "C",
|
||||
app::TemperatureType::Kelvin => "K",
|
||||
app::TemperatureType::Fahrenheit => "F",
|
||||
app::data_collection::temperature::TemperatureType::Celsius => "C",
|
||||
app::data_collection::temperature::TemperatureType::Kelvin => "K",
|
||||
app::data_collection::temperature::TemperatureType::Fahrenheit => "F",
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue