2019-09-11 03:37:20 +00:00
use tui ::{
layout ::{ Constraint , Direction , Layout } ,
2019-09-13 20:15:00 +00:00
style ::{ Color , Modifier , Style } ,
2019-09-11 03:37:20 +00:00
widgets ::{ Axis , Block , Borders , Chart , Dataset , Marker , Row , Table , Widget } ,
Terminal ,
} ;
2019-09-12 02:10:49 +00:00
2019-09-15 05:29:49 +00:00
use crate ::{ app , utils ::error } ;
2019-09-15 01:48:29 +00:00
2019-09-15 04:06:57 +00:00
const COLOUR_LIST : [ Color ; 6 ] = [ Color ::Red , Color ::Green , Color ::LightYellow , Color ::LightBlue , Color ::LightCyan , Color ::LightMagenta ] ;
2019-09-12 03:15:25 +00:00
const TEXT_COLOUR : Color = Color ::Gray ;
2019-09-12 03:23:14 +00:00
const GRAPH_COLOUR : Color = Color ::Gray ;
const BORDER_STYLE_COLOUR : Color = Color ::Gray ;
2019-09-15 04:06:57 +00:00
const GRAPH_MARKER : Marker = Marker ::Braille ;
2019-09-12 02:10:49 +00:00
#[ derive(Default) ]
pub struct CanvasData {
2019-09-15 04:06:57 +00:00
pub rx_display : String ,
pub tx_display : String ,
2019-09-15 01:48:29 +00:00
pub network_data_rx : Vec < ( f64 , f64 ) > ,
pub network_data_tx : Vec < ( f64 , f64 ) > ,
2019-09-12 02:10:49 +00:00
pub disk_data : Vec < Vec < String > > ,
pub temp_sensor_data : Vec < Vec < String > > ,
pub process_data : Vec < Vec < String > > ,
pub mem_data : Vec < ( f64 , f64 ) > ,
pub swap_data : Vec < ( f64 , f64 ) > ,
pub cpu_data : Vec < ( String , Vec < ( f64 , f64 ) > ) > ,
}
2019-09-16 20:18:42 +00:00
pub fn draw_data < B : tui ::backend ::Backend > ( terminal : & mut Terminal < B > , app_state : & mut app ::App , canvas_data : & CanvasData ) -> error ::Result < ( ) > {
2019-09-12 03:23:14 +00:00
let border_style : Style = Style ::default ( ) . fg ( BORDER_STYLE_COLOUR ) ;
2019-09-15 05:32:08 +00:00
let temperature_rows = canvas_data
. temp_sensor_data
. iter ( )
. map ( | sensor | Row ::StyledData ( sensor . iter ( ) , Style ::default ( ) . fg ( TEXT_COLOUR ) ) ) ;
2019-09-12 02:30:57 +00:00
let disk_rows = canvas_data . disk_data . iter ( ) . map ( | disk | Row ::StyledData ( disk . iter ( ) , Style ::default ( ) . fg ( TEXT_COLOUR ) ) ) ;
2019-09-12 02:10:49 +00:00
terminal . draw ( | mut f | {
2019-09-16 20:18:42 +00:00
//debug!("Drawing!");
2019-09-12 02:10:49 +00:00
let vertical_chunks = Layout ::default ( )
. direction ( Direction ::Vertical )
. margin ( 1 )
2019-09-15 05:29:49 +00:00
. constraints ( [ Constraint ::Percentage ( 34 ) , Constraint ::Percentage ( 34 ) , Constraint ::Percentage ( 33 ) ] . as_ref ( ) )
2019-09-12 02:10:49 +00:00
. split ( f . size ( ) ) ;
2019-09-12 03:15:25 +00:00
2019-09-12 02:10:49 +00:00
let middle_chunks = Layout ::default ( )
. direction ( Direction ::Horizontal )
. margin ( 0 )
2019-09-16 20:18:42 +00:00
. constraints ( [ Constraint ::Percentage ( 60 ) , Constraint ::Percentage ( 40 ) ] . as_ref ( ) )
2019-09-12 02:10:49 +00:00
. split ( vertical_chunks [ 1 ] ) ;
2019-09-15 01:48:29 +00:00
let middle_divided_chunk_2 = Layout ::default ( )
2019-09-12 02:10:49 +00:00
. direction ( Direction ::Vertical )
. margin ( 0 )
. constraints ( [ Constraint ::Percentage ( 50 ) , Constraint ::Percentage ( 50 ) ] . as_ref ( ) )
. split ( middle_chunks [ 1 ] ) ;
2019-09-12 03:15:25 +00:00
2019-09-12 02:10:49 +00:00
let bottom_chunks = Layout ::default ( )
. direction ( Direction ::Horizontal )
. margin ( 0 )
. constraints ( [ Constraint ::Percentage ( 50 ) , Constraint ::Percentage ( 50 ) ] . as_ref ( ) )
. split ( vertical_chunks [ 2 ] ) ;
// Set up blocks and their components
// CPU usage graph
{
2019-09-13 20:15:00 +00:00
let x_axis : Axis < String > = Axis ::default ( ) . style ( Style ::default ( ) . fg ( GRAPH_COLOUR ) ) . bounds ( [ 0.0 , 600_000.0 ] ) ;
2019-09-15 18:16:18 +00:00
let y_axis = Axis ::default ( ) . style ( Style ::default ( ) . fg ( GRAPH_COLOUR ) ) . bounds ( [ - 0.5 , 100.5 ] ) . labels ( & [ " 0% " , " 100% " ] ) ;
2019-09-12 02:10:49 +00:00
let mut dataset_vector : Vec < Dataset > = Vec ::new ( ) ;
2019-09-15 05:29:49 +00:00
2019-09-12 02:10:49 +00:00
for ( i , cpu ) in canvas_data . cpu_data . iter ( ) . enumerate ( ) {
2019-09-15 05:29:49 +00:00
let mut avg_cpu_exist_offset = 0 ;
2019-09-16 20:18:42 +00:00
if app_state . show_average_cpu {
2019-09-15 05:29:49 +00:00
if i = = 0 {
// Skip, we want to render the average cpu last!
continue ;
}
else {
avg_cpu_exist_offset = 1 ;
}
}
2019-09-12 02:10:49 +00:00
dataset_vector . push (
Dataset ::default ( )
. name ( & cpu . 0 )
2019-09-15 04:06:57 +00:00
. marker ( GRAPH_MARKER )
2019-09-15 05:29:49 +00:00
. style ( Style ::default ( ) . fg ( COLOUR_LIST [ i - avg_cpu_exist_offset % COLOUR_LIST . len ( ) ] ) )
2019-09-12 02:10:49 +00:00
. data ( & ( cpu . 1 ) ) ,
) ;
}
2019-09-16 20:18:42 +00:00
if ! canvas_data . cpu_data . is_empty ( ) & & app_state . show_average_cpu {
2019-09-15 05:29:49 +00:00
dataset_vector . push (
Dataset ::default ( )
. name ( & canvas_data . cpu_data [ 0 ] . 0 )
. marker ( GRAPH_MARKER )
. style ( Style ::default ( ) . fg ( COLOUR_LIST [ canvas_data . cpu_data . len ( ) - 1 % COLOUR_LIST . len ( ) ] ) )
. data ( & ( canvas_data . cpu_data [ 0 ] . 1 ) ) ,
) ;
}
2019-09-12 02:10:49 +00:00
Chart ::default ( )
2019-09-12 03:23:14 +00:00
. block ( Block ::default ( ) . title ( " CPU Usage " ) . borders ( Borders ::ALL ) . border_style ( border_style ) )
2019-09-12 02:10:49 +00:00
. x_axis ( x_axis )
. y_axis ( y_axis )
. datasets ( & dataset_vector )
2019-09-12 03:15:25 +00:00
. render ( & mut f , vertical_chunks [ 0 ] ) ;
2019-09-12 02:10:49 +00:00
}
//Memory usage graph
{
2019-09-13 20:15:00 +00:00
let x_axis : Axis < String > = Axis ::default ( ) . style ( Style ::default ( ) . fg ( GRAPH_COLOUR ) ) . bounds ( [ 0.0 , 600_000.0 ] ) ;
2019-09-15 18:16:18 +00:00
let y_axis = Axis ::default ( ) . style ( Style ::default ( ) . fg ( GRAPH_COLOUR ) ) . bounds ( [ - 0.5 , 100.5 ] ) . labels ( & [ " 0% " , " 100% " ] ) ; // Offset as the zero value isn't drawn otherwise...
2019-09-12 02:10:49 +00:00
Chart ::default ( )
2019-09-12 03:23:14 +00:00
. block ( Block ::default ( ) . title ( " Memory Usage " ) . borders ( Borders ::ALL ) . border_style ( border_style ) )
2019-09-12 02:10:49 +00:00
. x_axis ( x_axis )
. y_axis ( y_axis )
. datasets ( & [
Dataset ::default ( )
2019-09-15 04:06:57 +00:00
. name ( & ( " RAM: " . to_string ( ) + & format! ( " {:3} % " , ( canvas_data . mem_data . last ( ) . unwrap_or ( & ( 0_ f64 , 0_ f64 ) ) . 1. round ( ) as u64 ) ) ) )
. marker ( GRAPH_MARKER )
2019-09-15 01:48:29 +00:00
. style ( Style ::default ( ) . fg ( Color ::LightBlue ) )
2019-09-12 02:10:49 +00:00
. data ( & canvas_data . mem_data ) ,
Dataset ::default ( )
2019-09-15 04:06:57 +00:00
. name ( & ( " SWP: " . to_string ( ) + & format! ( " {:3} % " , ( canvas_data . swap_data . last ( ) . unwrap_or ( & ( 0_ f64 , 0_ f64 ) ) . 1. round ( ) as u64 ) ) ) )
. marker ( GRAPH_MARKER )
2019-09-15 01:48:29 +00:00
. style ( Style ::default ( ) . fg ( Color ::LightYellow ) )
2019-09-12 02:10:49 +00:00
. data ( & canvas_data . swap_data ) ,
] )
2019-09-12 03:15:25 +00:00
. render ( & mut f , middle_chunks [ 0 ] ) ;
2019-09-12 02:10:49 +00:00
}
// Temperature table
2019-09-14 20:46:14 +00:00
{
2019-09-15 01:48:29 +00:00
let width = f64 ::from ( middle_divided_chunk_2 [ 0 ] . width ) ;
2019-09-14 20:46:14 +00:00
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 ] )
2019-09-15 01:48:29 +00:00
. render ( & mut f , middle_divided_chunk_2 [ 0 ] ) ;
2019-09-14 20:46:14 +00:00
}
2019-09-12 02:10:49 +00:00
2019-09-14 20:46:14 +00:00
// Disk usage table
{
2019-09-15 18:16:18 +00:00
// TODO: We have to dynamically remove some of these table elements based on size...
2019-09-15 01:48:29 +00:00
let width = f64 ::from ( middle_divided_chunk_2 [ 1 ] . width ) ;
2019-09-15 18:16:18 +00:00
Table ::new ( [ " Disk " , " Mount " , " Used " , " Total " , " Free " , " R/s " , " W/s " ] . iter ( ) , disk_rows )
2019-09-14 20:46:14 +00:00
. block ( Block ::default ( ) . title ( " Disk Usage " ) . borders ( Borders ::ALL ) . border_style ( border_style ) )
. header_style ( Style ::default ( ) . fg ( Color ::LightBlue ) . modifier ( Modifier ::BOLD ) )
2019-09-15 05:32:08 +00:00
. widths ( & [
2019-09-16 20:18:42 +00:00
( width * 0.18 ) as u16 ,
( width * 0.14 ) as u16 ,
( width * 0.13 ) as u16 ,
( width * 0.13 ) as u16 ,
( width * 0.13 ) as u16 ,
( width * 0.13 ) as u16 ,
( width * 0.13 ) as u16 ,
2019-09-15 05:32:08 +00:00
] )
2019-09-15 01:48:29 +00:00
. render ( & mut f , middle_divided_chunk_2 [ 1 ] ) ;
}
// Network graph
{
let x_axis : Axis < String > = Axis ::default ( ) . style ( Style ::default ( ) . fg ( GRAPH_COLOUR ) ) . bounds ( [ 0.0 , 600_000.0 ] ) ;
2019-09-15 18:16:18 +00:00
let y_axis = Axis ::default ( ) . style ( Style ::default ( ) . fg ( GRAPH_COLOUR ) ) . bounds ( [ - 0.5 , 1_000_000.5 ] ) . labels ( & [ " 0GB " , " 1GB " ] ) ;
2019-09-15 01:48:29 +00:00
Chart ::default ( )
. block ( Block ::default ( ) . title ( " Network " ) . borders ( Borders ::ALL ) . border_style ( border_style ) )
. x_axis ( x_axis )
. y_axis ( y_axis )
. datasets ( & [
Dataset ::default ( )
2019-09-15 04:06:57 +00:00
. name ( & ( canvas_data . rx_display ) )
. marker ( GRAPH_MARKER )
2019-09-15 01:48:29 +00:00
. style ( Style ::default ( ) . fg ( Color ::LightBlue ) )
. data ( & canvas_data . network_data_rx ) ,
Dataset ::default ( )
2019-09-15 04:06:57 +00:00
. name ( & ( canvas_data . tx_display ) )
. marker ( GRAPH_MARKER )
2019-09-15 01:48:29 +00:00
. style ( Style ::default ( ) . fg ( Color ::LightYellow ) )
. data ( & canvas_data . network_data_tx ) ,
] )
. render ( & mut f , bottom_chunks [ 0 ] ) ;
2019-09-14 20:46:14 +00:00
}
2019-09-12 02:10:49 +00:00
// Processes table
2019-09-14 20:46:14 +00:00
{
let width = f64 ::from ( bottom_chunks [ 1 ] . width ) ;
2019-09-16 20:18:42 +00:00
// Admittedly this is kinda a hack... but we need to:
// * Scroll
// * Show/hide elements based on scroll position
// As such, we use a process_counter to know when we've hit the process we've currently scrolled to. We also need to move the list - we can
// do so by hiding some elements!
let num_rows = i64 ::from ( bottom_chunks [ 1 ] . height ) - 3 ;
let mut process_counter = 0 ;
//TODO: Fix this!
2019-09-16 22:47:49 +00:00
let start_position = match app_state . scroll_direction {
app ::ScrollDirection ::DOWN = > {
if app_state . currently_selected_process_position < num_rows {
0
}
else if app_state . currently_selected_process_position - num_rows < app_state . previous_process_position {
app_state . previous_process_position
}
else {
app_state . previous_process_position = app_state . currently_selected_process_position - num_rows + 1 ;
app_state . previous_process_position
}
}
app ::ScrollDirection ::UP = > {
if app_state . currently_selected_process_position = = app_state . previous_process_position - 1 {
app_state . previous_process_position = if app_state . previous_process_position > 0 {
app_state . previous_process_position - 1
2019-09-16 20:18:42 +00:00
}
else {
2019-09-16 22:47:49 +00:00
0
} ;
app_state . previous_process_position
2019-09-16 20:18:42 +00:00
}
2019-09-16 22:47:49 +00:00
else {
app_state . previous_process_position
2019-09-16 20:18:42 +00:00
}
}
} ;
2019-09-16 23:05:44 +00:00
/* debug!(
2019-09-16 22:47:49 +00:00
" START POSN: {}, PREV POSN: {}, CURRENT SELECTED POSN: {}, NUM ROWS: {} " ,
start_position , app_state . previous_process_position , app_state . currently_selected_process_position , num_rows
2019-09-16 23:05:44 +00:00
) ; * /
2019-09-16 20:18:42 +00:00
let sliced_vec : Vec < Vec < String > > = ( & canvas_data . process_data [ start_position as usize .. ] ) . to_vec ( ) ;
let process_rows = sliced_vec . iter ( ) . map ( | process | {
Row ::StyledData (
process . iter ( ) ,
if process_counter = = app_state . currently_selected_process_position - start_position {
// TODO: This is what controls the highlighting!
process_counter = - 1 ;
Style ::default ( ) . fg ( Color ::Black ) . bg ( Color ::Cyan )
}
else {
if process_counter > = 0 {
process_counter + = 1 ;
}
Style ::default ( ) . fg ( TEXT_COLOUR )
} ,
)
} ) ;
2019-09-14 20:46:14 +00:00
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 ] ) ;
}
2019-09-12 02:10:49 +00:00
} ) ? ;
2019-09-16 20:18:42 +00:00
//debug!("Finished drawing.");
2019-09-12 02:10:49 +00:00
Ok ( ( ) )
}