summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClementTsang <clementjhtsang@gmail.com>2019-09-08 19:56:23 -0400
committerClementTsang <clementjhtsang@gmail.com>2019-09-08 19:56:23 -0400
commit471209f511f9a23edf35529cf04cd6fa23e1b408 (patch)
tree4b83350552c30a7667c26f2d7c2ee702fc966177
parentd80c2387fb70f6a2c1234b211cf677711e280cba (diff)
Refactored code such that it fits more of tui-rs' example.
-rw-r--r--Cargo.toml9
-rw-r--r--src/main.rs186
-rw-r--r--src/widgets/disks.rs18
-rw-r--r--src/widgets/mod.rs101
-rw-r--r--src/widgets/processes.rs10
-rw-r--r--src/window/mod.rs52
6 files changed, 226 insertions, 150 deletions
diff --git a/Cargo.toml b/Cargo.toml
index ab26928e..42cc4b60 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,12 +7,17 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+crossterm = "0.10.2"
futures-preview = "0.3.0-alpha.18"
futures-timer = "0.3"
futures-util = "0.2.1"
heim = "0.0.7"
heim-common = "0.0.7"
sysinfo = "0.9.4"
-termion = "1.5.3"
tokio = "0.2.0-alpha.4"
-tui = "0.6.2"
+
+
+[dependencies.tui]
+version = "0.6.2"
+default-features = false
+features = ['crossterm']
diff --git a/src/main.rs b/src/main.rs
index 520f473c..e7ce7efb 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,92 +1,120 @@
-use sysinfo::{System, SystemExt};
+use crossterm::{input, AlternateScreen, InputEvent, KeyEvent};
+use std::{
+ io::{self, stdin, stdout, Write},
+ sync::mpsc,
+ thread,
+ time::Duration,
+};
+use tui::{
+ backend::CrosstermBackend,
+ layout::{Constraint, Direction, Layout},
+ widgets::{Block, Borders, Widget},
+ Terminal,
+};
mod widgets;
-use widgets::{cpu, disks, mem, network, processes, temperature};
-mod window;
-
-fn set_if_valid<T : std::clone::Clone>(result : &Result<T, heim::Error>, value_to_set : &mut T) {
- if let Ok(result) = result {
- *value_to_set = (*result).clone();
- }
+enum Event<I> {
+ Input(I),
+ Tick,
}
#[tokio::main]
-async fn main() -> Result<(), std::io::Error> {
- // Initialize
- let refresh_interval = 1; // TODO: Make changing this possible!
- let mut sys = System::new();
-
- let mut list_of_cpu_packages : Vec<cpu::CPUData> = Vec::new();
- let mut list_of_io : Vec<disks::IOInfo> = Vec::new();
- let mut list_of_physical_io : Vec<disks::IOInfo> = Vec::new();
- let mut memory : mem::MemData = mem::MemData::default();
- let mut swap : mem::MemData = mem::MemData::default();
- let mut list_of_temperature : Vec<temperature::TempData> = Vec::new();
- let mut network : network::NetworkData = network::NetworkData::default();
- let mut list_of_processes = Vec::new();
- let mut list_of_disks = Vec::new();
+async fn main() -> Result<(), io::Error> {
+ let screen = AlternateScreen::to_alternate(true)?;
+ let backend = CrosstermBackend::with_alternate_screen(screen)?;
+ let mut terminal = Terminal::new(backend)?;
+ terminal.hide_cursor()?;
+ // Setup input handling
+ let (tx, rx) = mpsc::channel();
+ {
+ let tx = tx.clone();
+ thread::spawn(move || {
+ let input = input();
+ let reader = input.read_sync();
+ for event in reader {
+ if let InputEvent::Keyboard(key) = event {
+ if tx.send(Event::Input(key.clone())).is_err() {
+ return;
+ }
+ }
+ }
+ });
+ }
+ {
+ let tx = tx.clone();
+ thread::spawn(move || {
+ let tx = tx.clone();
+ loop {
+ tx.send(Event::Tick).unwrap();
+ thread::sleep(Duration::from_millis(250));
+ }
+ });
+ }
- window::create_terminal()?;
+ let mut app : widgets::App = widgets::App::new("rustop");
+ terminal.clear()?;
loop {
- sys.refresh_system();
- sys.refresh_network();
-
- // What we want to do: For timed data, if there is an error, just do not add. For other data, just don't update!
- set_if_valid(&network::get_network_data(&sys), &mut network);
- set_if_valid(&cpu::get_cpu_data_list(&sys), &mut list_of_cpu_packages);
-
- // TODO: Joining all futures would be better...
- set_if_valid(&processes::get_sorted_processes_list(processes::ProcessSorting::NAME, false).await, &mut list_of_processes);
- set_if_valid(&disks::get_disk_usage_list().await, &mut list_of_disks);
- set_if_valid(&disks::get_io_usage_list(false).await, &mut list_of_io);
- set_if_valid(&disks::get_io_usage_list(true).await, &mut list_of_physical_io);
- set_if_valid(&mem::get_mem_data_list().await, &mut memory);
- set_if_valid(&mem::get_swap_data_list().await, &mut swap);
- set_if_valid(&temperature::get_temperature_data().await, &mut list_of_temperature);
-
- /*
- // DEBUG - output results
- for process in &list_of_processes {
- println!(
- "Process: {} with PID {}, CPU: {}%, MEM: {} MB",
- process.command, process.pid, process.cpu_usage_percent, process.mem_usage_in_mb,
- );
- }
- for disk in &list_of_disks {
- println!("{} is mounted on {}: {} used.", disk.name, disk.mount_point, disk.used_space as f64 / disk.total_space as f64);
- // TODO: Check if this is valid
- }
-
- for io in &list_of_io {
- println!("IO counter for {}: {} writes, {} reads.", &io.mount_point, io.write_bytes, io.read_bytes);
+ terminal.draw(|mut f| {
+ let vertical_chunks = Layout::default()
+ .direction(Direction::Vertical)
+ .margin(1)
+ .constraints([Constraint::Percentage(33), Constraint::Percentage(34), Constraint::Percentage(33)].as_ref())
+ .split(f.size());
+ let top_chunks = Layout::default()
+ .direction(Direction::Horizontal)
+ .margin(0)
+ .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
+ .split(vertical_chunks[0]);
+ let middle_chunks = Layout::default()
+ .direction(Direction::Horizontal)
+ .margin(0)
+ .constraints([Constraint::Percentage(40), Constraint::Percentage(60)].as_ref())
+ .split(vertical_chunks[1]);
+ let middle_divided_chunk = Layout::default()
+ .direction(Direction::Vertical)
+ .margin(0)
+ .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
+ .split(middle_chunks[0]);
+ let bottom_chunks = Layout::default()
+ .direction(Direction::Horizontal)
+ .margin(0)
+ .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
+ .split(vertical_chunks[2]);
+
+ Block::default().title("CPU Usage").borders(Borders::ALL).render(&mut f, top_chunks[0]);
+ Block::default().title("Memory Usage").borders(Borders::ALL).render(&mut f, top_chunks[1]);
+
+ Block::default().title("Temperatures").borders(Borders::ALL).render(&mut f, middle_divided_chunk[0]);
+ Block::default().title("Disk Usage").borders(Borders::ALL).render(&mut f, middle_divided_chunk[1]);
+ Block::default().title("IO Usage").borders(Borders::ALL).render(&mut f, middle_chunks[1]);
+
+ Block::default().title("Network").borders(Borders::ALL).render(&mut f, bottom_chunks[0]);
+ Block::default().title("Processes").borders(Borders::ALL).render(&mut f, bottom_chunks[1]);
+ })?;
+
+ // TODO: Ctrl-C?
+ if let Ok(recv) = rx.recv() {
+ match recv {
+ Event::Input(event) => match event {
+ KeyEvent::Char(c) => app.on_key(c),
+ KeyEvent::Left => {}
+ KeyEvent::Right => {}
+ KeyEvent::Up => {}
+ KeyEvent::Down => {}
+ KeyEvent::Ctrl('c') => break,
+ _ => {}
+ },
+ Event::Tick => {
+ app.update_data();
+ }
+ }
+ if app.should_quit {
+ break;
+ }
}
-
- for io in &list_of_physical_io {
- println!("Physical IO counter for {}: {} writes, {} reads.", &io.mount_point, io.write_bytes, io.read_bytes);
- }
-
- for cpu in &list_of_cpu_packages {
- println!("CPU {} has {}% usage!", &cpu.cpu_name, cpu.cpu_usage);
- }
-
- println!("Memory usage: {} out of {} is used", memory.mem_used, memory.mem_total);
-
- println!("Memory usage: {} out of {} is used", swap.mem_used, swap.mem_total);
-
- for sensor in &list_of_temperature {
- println!("Sensor for {} is at {} degrees Celsius", sensor.component_name, sensor.temperature);
- }
-
- println!("Network: {} rx, {} tx", network.rx, network.tx);
- */
-
- // TODO: Send to drawing module
-
- // Repeat on interval
- std::thread::sleep(std::time::Duration::from_secs(refresh_interval));
}
- // TODO: Exit on quit command/ctrl-c
+ Ok(())
}
diff --git a/src/widgets/disks.rs b/src/widgets/disks.rs
index 7a3ed212..0d8f26c5 100644
--- a/src/widgets/disks.rs
+++ b/src/widgets/disks.rs
@@ -1,7 +1,7 @@
use heim_common::prelude::StreamExt;
#[derive(Clone, Default)]
-pub struct DiskInfo {
+pub struct DiskData {
pub name : Box<str>,
pub mount_point : Box<str>,
pub free_space : u64,
@@ -10,19 +10,19 @@ pub struct DiskInfo {
}
#[derive(Clone, Default)]
-pub struct IOInfo {
+pub struct IOData {
pub mount_point : Box<str>,
pub read_bytes : u64,
pub write_bytes : u64,
}
-pub async fn get_io_usage_list(get_physical : bool) -> Result<Vec<IOInfo>, heim::Error> {
- let mut io_list : Vec<IOInfo> = Vec::new();
+pub async fn get_io_usage_list(get_physical : bool) -> Result<Vec<IOData>, heim::Error> {
+ let mut io_list : Vec<IOData> = Vec::new();
if get_physical {
let mut physical_counter_stream = heim::disk::io_counters_physical();
while let Some(io) = physical_counter_stream.next().await {
let io = io?;
- io_list.push(IOInfo {
+ io_list.push(IOData {
mount_point : Box::from(io.device_name().to_str().unwrap_or("Name Unavailable")),
read_bytes : io.read_bytes().get::<heim_common::units::information::megabyte>(),
write_bytes : io.write_bytes().get::<heim_common::units::information::megabyte>(),
@@ -33,7 +33,7 @@ pub async fn get_io_usage_list(get_physical : bool) -> Result<Vec<IOInfo>, heim:
let mut counter_stream = heim::disk::io_counters();
while let Some(io) = counter_stream.next().await {
let io = io?;
- io_list.push(IOInfo {
+ io_list.push(IOData {
mount_point : Box::from(io.device_name().to_str().unwrap_or("Name Unavailable")),
read_bytes : io.read_bytes().get::<heim_common::units::information::megabyte>(),
write_bytes : io.write_bytes().get::<heim_common::units::information::megabyte>(),
@@ -44,15 +44,15 @@ pub async fn get_io_usage_list(get_physical : bool) -> Result<Vec<IOInfo>, heim:
Ok(io_list)
}
-pub async fn get_disk_usage_list() -> Result<Vec<DiskInfo>, heim::Error> {
- let mut vec_disks : Vec<DiskInfo> = Vec::new();
+pub async fn get_disk_usage_list() -> Result<Vec<DiskData>, heim::Error> {
+ let mut vec_disks : Vec<DiskData> = Vec::new();
let mut partitions_stream = heim::disk::partitions_physical();
while let Some(part) = partitions_stream.next().await {
let partition = part?; // TODO: Change this? We don't want to error out immediately...
let usage = heim::disk::usage(partition.mount_point().to_path_buf()).await?;
- vec_disks.push(DiskInfo {
+ vec_disks.push(DiskData {
free_space : usage.free().get::<heim_common::units::information::megabyte>(),
used_space : usage.used().get::<heim_common::units::information::megabyte>(),
total_space : usage.total().get::<heim_common::units::information::megabyte>(),
diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs
index cf9c9d2a..c99832c1 100644
--- a/src/widgets/mod.rs
+++ b/src/widgets/mod.rs
@@ -1,6 +1,101 @@
+pub mod cpu;
pub mod disks;
-pub mod temperature;
+pub mod mem;
pub mod network;
pub mod processes;
-pub mod mem;
-pub mod cpu; \ No newline at end of file
+pub mod temperature;
+
+use sysinfo::{System, SystemExt};
+
+#[derive(Default)]
+pub struct App<'a> {
+ pub should_quit : bool,
+ pub list_of_cpu_packages : Vec<cpu::CPUData>,
+ pub list_of_io : Vec<disks::IOData>,
+ pub list_of_physical_io : Vec<disks::IOData>,
+ pub memory : mem::MemData,
+ pub swap : mem::MemData,
+ pub list_of_temperature : Vec<temperature::TempData>,
+ pub network : network::NetworkData,
+ pub list_of_processes : Vec<processes::ProcessData>,
+ pub list_of_disks : Vec<disks::DiskData>,
+ pub title : &'a str,
+}
+
+fn set_if_valid<T : std::clone::Clone>(result : &Result<T, heim::Error>, value_to_set : &mut T) {
+ if let Ok(result) = result {
+ *value_to_set = (*result).clone();
+ }
+}
+
+impl<'a> App<'a> {
+ pub fn new(title : &str) -> App {
+ let mut app = App::default();
+ app.title = title;
+ app
+ }
+
+ pub fn on_key(&mut self, c : char) {
+ match c {
+ 'q' => self.should_quit = true,
+ _ => {}
+ }
+ }
+
+ pub async fn update_data(&mut self) {
+ // Initialize
+ let mut sys = System::new();
+
+ sys.refresh_system();
+ sys.refresh_network();
+
+ // What we want to do: For timed data, if there is an error, just do not add. For other data, just don't update!
+ set_if_valid(&network::get_network_data(&sys), &mut self.network);
+ set_if_valid(&cpu::get_cpu_data_list(&sys), &mut self.list_of_cpu_packages);
+
+ // TODO: Joining all futures would be better...
+ set_if_valid(&processes::get_sorted_processes_list(processes::ProcessSorting::NAME, false).await, &mut self.list_of_processes);
+ set_if_valid(&disks::get_disk_usage_list().await, &mut self.list_of_disks);
+ set_if_valid(&disks::get_io_usage_list(false).await, &mut self.list_of_io);
+ set_if_valid(&disks::get_io_usage_list(true).await, &mut self.list_of_physical_io);
+ set_if_valid(&mem::get_mem_data_list().await, &mut self.memory);
+ set_if_valid(&mem::get_swap_data_list().await, &mut self.swap);
+ set_if_valid(&temperature::get_temperature_data().await, &mut self.list_of_temperature);
+
+ /*
+ // DEBUG - output results
+ for process in &list_of_processes {
+ println!(
+ "Process: {} with PID {}, CPU: {}%, MEM: {} MB",
+ process.command, process.pid, process.cpu_usage_percent, process.mem_usage_in_mb,
+ );
+ }
+ for disk in &list_of_disks {
+ println!("{} is mounted on {}: {} used.", disk.name, disk.mount_point, disk.used_space as f64 / disk.total_space as f64);
+ // TODO: Check if this is valid
+ }
+
+ for io in &list_of_io {
+ println!("IO counter for {}: {} writes, {} reads.", &io.mount_point, io.write_bytes, io.read_bytes);
+ }
+
+ for io in &list_of_physical_io {
+ println!("Physical IO counter for {}: {} writes, {} reads.", &io.mount_point, io.write_bytes, io.read_bytes);
+ }
+
+ for cpu in &list_of_cpu_packages {
+ println!("CPU {} has {}% usage!", &cpu.cpu_name, cpu.cpu_usage);
+ }
+
+ println!("Memory usage: {} out of {} is used", memory.mem_used, memory.mem_total);
+
+ println!("Memory usage: {} out of {} is used", swap.mem_used, swap.mem_total);
+
+ for sensor in &list_of_temperature {
+ println!("Sensor for {} is at {} degrees Celsius", sensor.component_name, sensor.temperature);
+ }
+
+ println!("Network: {} rx, {} tx", network.rx, network.tx);
+ */
+ }
+}
diff --git a/src/widgets/processes.rs b/src/widgets/processes.rs
index b4e90d4d..20f6b3b2 100644
--- a/src/widgets/processes.rs
+++ b/src/widgets/processes.rs
@@ -13,7 +13,7 @@ pub enum ProcessSorting {
// Possible process info struct?
#[derive(Clone, Default)]
-pub struct ProcessInfo {
+pub struct ProcessData {
pub pid : u32,
pub cpu_usage_percent : f32,
pub mem_usage_in_mb : u64,
@@ -50,10 +50,10 @@ async fn cpu_usage(process : heim::process::Process) -> heim::process::ProcessRe
Ok((process, usage_2 - usage_1))
}
-pub async fn get_sorted_processes_list(sorting_method : ProcessSorting, reverse_order : bool) -> Result<Vec<ProcessInfo>, heim::Error> {
+pub async fn get_sorted_processes_list(sorting_method : ProcessSorting, reverse_order : bool) -> Result<Vec<ProcessData>, heim::Error> {
let mut process_stream = heim::process::processes().map_ok(cpu_usage).try_buffer_unordered(std::usize::MAX);
- let mut process_vector : Vec<ProcessInfo> = Vec::new();
+ let mut process_vector : Vec<ProcessData> = Vec::new();
while let Some(process) = process_stream.next().await {
if let Ok(process) = process {
let (process, cpu_usage) = process;
@@ -69,7 +69,7 @@ pub async fn get_sorted_processes_list(sorting_method : ProcessSorting, reverse_
});
*/
- process_vector.push(ProcessInfo {
+ process_vector.push(ProcessData {
command : process.name().await.unwrap_or_else(|_| "".to_string()),
pid : process.pid() as u32,
cpu_usage_percent : cpu_usage.get::<units::ratio::percent>(),
@@ -83,7 +83,7 @@ pub async fn get_sorted_processes_list(sorting_method : ProcessSorting, reverse_
Ok(process_vector)
}
-pub fn sort_processes(sorting_method : ProcessSorting, process_vector : &mut Vec<ProcessInfo>, reverse_order : bool) {
+pub fn sort_processes(sorting_method : ProcessSorting, process_vector : &mut Vec<ProcessData>, reverse_order : bool) {
match sorting_method {
ProcessSorting::CPU => process_vector.sort_by(|a, b| get_ordering(a.cpu_usage_percent, b.cpu_usage_percent, reverse_order)),
ProcessSorting::MEM => process_vector.sort_by(|a, b| get_ordering(a.mem_usage_in_mb, b.mem_usage_in_mb, reverse_order)),
diff --git a/src/window/mod.rs b/src/window/mod.rs
deleted file mode 100644
index bfb6a46f..00000000
--- a/src/window/mod.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-use std::io;
-use termion::raw::IntoRawMode;
-use tui::{
- backend::TermionBackend,
- layout::{Constraint, Direction, Layout},
- widgets::{Block, Borders, Widget},
- Terminal,
-};
-
-pub fn create_terminal() -> Result<(), io::Error> {
- let stdout = io::stdout().into_raw_mode()?;
- let backend = TermionBackend::new(stdout);
- let mut terminal = Terminal::new(backend)?;
- terminal.clear()?;
- terminal.draw(|mut f| {
- let vertical_chunks = Layout::default()
- .direction(Direction::Vertical)
- .margin(1)
- .constraints([Constraint::Percentage(33), Constraint::Percentage(34), Constraint::Percentage(33)].as_ref())
- .split(f.size());
- let top_chunks = Layout::default()
- .direction(Direction::Horizontal)
- .margin(0)
- .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
- .split(vertical_chunks[0]);
- let middle_chunks = Layout::default()
- .direction(Direction::Horizontal)
- .margin(0)
- .constraints([Constraint::Percentage(40), Constraint::Percentage(60)].as_ref())
- .split(vertical_chunks[1]);
- let middle_divided_chunk = Layout::default()
- .direction(Direction::Vertical)
- .margin(0)
- .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
- .split(middle_chunks[0]);
- let bottom_chunks = Layout::default()
- .direction(Direction::Horizontal)
- .margin(0)
- .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
- .split(vertical_chunks[2]);
-
- Block::default().title("CPU Usage").borders(Borders::ALL).render(&mut f, top_chunks[0]);
- Block::default().title("Memory Usage").borders(Borders::ALL).render(&mut f, top_chunks[1]);
-
- Block::default().title("Temperatures").borders(Borders::ALL).render(&mut f, middle_divided_chunk[0]);
- Block::default().title("Disk Usage").borders(Borders::ALL).render(&mut f, middle_divided_chunk[1]);
- Block::default().title("IO Usage").borders(Borders::ALL).render(&mut f, middle_chunks[1]);
-
- Block::default().title("Network").borders(Borders::ALL).render(&mut f, bottom_chunks[0]);
- Block::default().title("Processes").borders(Borders::ALL).render(&mut f, bottom_chunks[1]);
- })
-}