Cleanup before modularity ()

* Uptick some crates, update README dependencies

* Cleanup before modularity feature.

* Fix missing reset zoom on reset

* Fixed reset... not resetting search or data displayed

* Cleaned up options a tiny bit to make more sense.

* Cleaned up some TODOs and the like.

* specify only build master branch.
This commit is contained in:
Clement Tsang 2020-03-13 01:07:24 -04:00 committed by GitHub
parent d9747f78e8
commit 1968bb14b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 326 additions and 290 deletions

@ -11,6 +11,9 @@ jobs:
- rust: nightly
- env: TARGET=x86_64-pc-windows-gnu # Seems to cause problems. TODO: Add test for it, but keep allow fail.
fast_finish: true
branches:
only:
- master
before_install:
- export RUST_BACKTRACE=1

@ -23,7 +23,8 @@ lto = "fat"
codegen-units = 1
[dependencies]
chrono = "0.4.10"
crossterm = "0.16"
chrono = "0.4.11"
clap = "2.33.0"
dirs = "2.0.2"
fern = "0.6.0"
@ -32,11 +33,11 @@ heim = "0.0.10"
log = "0.4.8"
regex = "1.3.4"
sysinfo = "0.11"
crossterm = "0.16"
toml = "0.5.6"
tui = {version = "0.8", features = ["crossterm"], default-features = false }
typed-builder = "0.5.1"
lazy_static = "1.4.0"
backtrace = "0.3"
toml = "0.5.6"
serde = {version = "1.0", features = ["derive"] }
unicode-segmentation = "1.6.0"
unicode-width = "0.1.7"

@ -42,7 +42,7 @@ For information about config files, see [this document](./docs/config.md) for mo
## Installation
In all cases you can install the in-development version by cloning from this repo and using `cargo build --release`. This is built and tested with Rust Stable (1.41 as of writing).
In all cases you can install the in-development version by cloning from this repo and using `cargo build --release`. This is built and tested with Rust Stable (1.42 as of writing).
In addition to the below methods, you can manually build from the [Releases](https://github.com/ClementTsang/bottom/releases) page by downloading and building.
@ -257,13 +257,13 @@ Thanks to those who have contributed:
- [dirs](https://github.com/soc/dirs-rs)
- [fern](https://github.com/daboross/fern)
- [futures-rs](https://github.com/rust-lang-nursery/futures-rs)
- [futures-timer](https://github.com/rustasync/futures-timer)
- [heim](https://github.com/heim-rs/heim)
- [lazy_static](https://github.com/rust-lang-nursery/lazy-static.rs)
- [log](https://github.com/rust-lang-nursery/log)
- [serde](https://github.com/serde-rs/serde)
- [sysinfo](https://github.com/GuillaumeGomez/sysinfo)
- [tokio](https://github.com/tokio-rs/tokio)
- [toml-rs](https://github.com/alexcrichton/toml-rs)
- [typed-builder](https://github.com/idanarye/rust-typed-builder)
- [tui-rs](https://github.com/fdehau/tui-rs)
- [unicode-segmentation](https://github.com/unicode-rs/unicode-segmentation)
- [unicode-width](https://github.com/unicode-rs/unicode-width)

@ -4,6 +4,8 @@ use std::time::Instant;
use unicode_segmentation::GraphemeCursor;
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
use typed_builder::*;
use data_farmer::*;
use data_harvester::{processes, temperature};
@ -108,8 +110,8 @@ impl Default for AppScrollState {
/// AppSearchState deals with generic searching (I might do this in the future).
pub struct AppSearchState {
pub is_enabled: bool,
current_search_query: String,
current_regex: Option<std::result::Result<regex::Regex, regex::Error>>,
pub current_search_query: String,
pub current_regex: Option<std::result::Result<regex::Regex, regex::Error>>,
pub is_blank_search: bool,
pub is_invalid_search: bool,
pub grapheme_cursor: GraphemeCursor,
@ -137,10 +139,11 @@ impl Default for AppSearchState {
impl AppSearchState {
/// Returns a reset but still enabled app search state
pub fn reset() -> Self {
let mut app_search_state = AppSearchState::default();
app_search_state.is_enabled = true;
app_search_state
pub fn reset(&mut self) {
*self = AppSearchState {
is_enabled: self.is_enabled,
..AppSearchState::default()
}
}
pub fn is_invalid_or_blank_search(&self) -> bool {
@ -211,7 +214,6 @@ impl Default for AppHelpDialogState {
/// AppConfigFields is meant to cover basic fields that would normally be set
/// by config files or launch options.
#[derive(Default)]
pub struct AppConfigFields {
pub update_rate_in_milliseconds: u64,
pub temperature_type: temperature::TemperatureType,
@ -233,21 +235,21 @@ pub struct NetState {
pub is_showing_rx: bool,
pub is_showing_tx: bool,
pub zoom_level: f64,
pub display_time: u64,
pub current_display_time: u64,
pub force_update: bool,
pub display_time_instant: Option<Instant>,
pub autohide_timer: Option<Instant>,
}
impl Default for NetState {
fn default() -> Self {
impl NetState {
pub fn init(current_display_time: u64, autohide_timer: Option<Instant>) -> Self {
NetState {
is_showing_tray: false,
is_showing_rx: true,
is_showing_tx: true,
zoom_level: 100.0,
display_time: constants::DEFAULT_TIME_MILLISECONDS,
current_display_time,
force_update: false,
display_time_instant: None,
autohide_timer,
}
}
}
@ -258,21 +260,21 @@ pub struct CpuState {
pub zoom_level: f64,
pub core_show_vec: Vec<bool>,
pub num_cpus_shown: u64,
pub display_time: u64,
pub current_display_time: u64,
pub force_update: bool,
pub display_time_instant: Option<Instant>,
pub autohide_timer: Option<Instant>,
}
impl Default for CpuState {
fn default() -> Self {
impl CpuState {
pub fn init(current_display_time: u64, autohide_timer: Option<Instant>) -> Self {
CpuState {
is_showing_tray: false,
zoom_level: 100.0,
core_show_vec: Vec::new(),
num_cpus_shown: 0,
display_time: constants::DEFAULT_TIME_MILLISECONDS,
current_display_time,
force_update: false,
display_time_instant: None,
autohide_timer,
}
}
}
@ -283,136 +285,117 @@ pub struct MemState {
pub is_showing_ram: bool,
pub is_showing_swap: bool,
pub zoom_level: f64,
pub display_time: u64,
pub current_display_time: u64,
pub force_update: bool,
pub display_time_instant: Option<Instant>,
pub autohide_timer: Option<Instant>,
}
impl Default for MemState {
fn default() -> Self {
impl MemState {
pub fn init(current_display_time: u64, autohide_timer: Option<Instant>) -> Self {
MemState {
is_showing_tray: false,
is_showing_ram: true,
is_showing_swap: true,
zoom_level: 100.0,
display_time: constants::DEFAULT_TIME_MILLISECONDS,
current_display_time,
force_update: false,
display_time_instant: None,
autohide_timer,
}
}
}
#[derive(TypedBuilder)]
pub struct App {
#[builder(default=processes::ProcessSorting::CPU, setter(skip))]
pub process_sorting_type: processes::ProcessSorting,
#[builder(default = true, setter(skip))]
pub process_sorting_reverse: bool,
#[builder(default = false, setter(skip))]
pub force_update_processes: bool,
#[builder(default, setter(skip))]
pub app_scroll_positions: AppScrollState,
pub current_widget_selected: WidgetPosition,
pub previous_basic_table_selected: WidgetPosition,
#[builder(default = false, setter(skip))]
awaiting_second_char: bool,
#[builder(default, setter(skip))]
second_char: Option<char>,
#[builder(default, setter(skip))]
pub dd_err: Option<String>,
#[builder(default, setter(skip))]
to_delete_process_list: Option<(String, Vec<u32>)>,
#[builder(default = false, setter(skip))]
pub is_frozen: bool,
#[builder(default = Instant::now(), setter(skip))]
last_key_press: Instant,
#[builder(default, setter(skip))]
pub canvas_data: canvas::DisplayableData,
#[builder(default = false)]
enable_grouping: bool,
#[builder(default, setter(skip))]
pub data_collection: DataCollection,
#[builder(default, setter(skip))]
pub process_search_state: ProcessSearchState,
#[builder(default, setter(skip))]
pub delete_dialog_state: AppDeleteDialogState,
#[builder(default, setter(skip))]
pub help_dialog_state: AppHelpDialogState,
pub app_config_fields: AppConfigFields,
#[builder(default = false, setter(skip))]
pub is_expanded: bool,
#[builder(default = false, setter(skip))]
pub is_resized: bool,
pub cpu_state: CpuState,
pub mem_state: MemState,
pub net_state: NetState,
pub app_config_fields: AppConfigFields,
pub current_widget_selected: WidgetPosition,
pub previous_basic_table_selected: WidgetPosition,
}
impl App {
#[allow(clippy::too_many_arguments)]
// TODO: [REFACTOR] use builder pattern instead.
pub fn new(
show_average_cpu: bool, temperature_type: temperature::TemperatureType,
update_rate_in_milliseconds: u64, use_dot: bool, left_legend: bool,
use_current_cpu_total: bool, current_widget_selected: WidgetPosition,
show_disabled_data: bool, use_basic_mode: bool, default_time_value: u64,
time_interval: u64,
) -> App {
let mut cpu_state = CpuState::default();
let mut mem_state = MemState::default();
let mut net_state = NetState::default();
cpu_state.display_time = default_time_value;
mem_state.display_time = default_time_value;
net_state.display_time = default_time_value;
App {
process_sorting_type: processes::ProcessSorting::CPU,
process_sorting_reverse: true,
force_update_processes: false,
current_widget_selected: if use_basic_mode {
match current_widget_selected {
WidgetPosition::Cpu => WidgetPosition::BasicCpu,
WidgetPosition::Network => WidgetPosition::BasicNet,
WidgetPosition::Mem => WidgetPosition::BasicMem,
_ => current_widget_selected,
}
} else {
current_widget_selected
},
previous_basic_table_selected: if current_widget_selected.is_widget_table() {
current_widget_selected
} else {
WidgetPosition::Process
},
app_scroll_positions: AppScrollState::default(),
awaiting_second_char: false,
second_char: None,
dd_err: None,
to_delete_process_list: None,
is_frozen: false,
last_key_press: Instant::now(),
canvas_data: canvas::DisplayableData::default(),
enable_grouping: false,
data_collection: DataCollection::default(),
process_search_state: ProcessSearchState::default(),
delete_dialog_state: AppDeleteDialogState::default(),
help_dialog_state: AppHelpDialogState::default(),
app_config_fields: AppConfigFields {
show_average_cpu,
temperature_type,
use_dot,
update_rate_in_milliseconds,
left_legend,
use_current_cpu_total,
show_disabled_data,
use_basic_mode,
default_time_value,
time_interval,
hide_time: false,
autohide_time: false,
},
is_expanded: false,
is_resized: false,
cpu_state,
mem_state,
net_state,
}
}
pub fn reset(&mut self) {
// Reset multi
self.reset_multi_tap_keys();
// Reset dialog state
self.help_dialog_state.is_showing_help = false;
self.delete_dialog_state.is_showing_dd = false;
if self.process_search_state.search_state.is_enabled {
self.current_widget_selected = WidgetPosition::Process;
self.process_search_state.search_state.is_enabled = false;
}
self.process_search_state.search_state.current_search_query = String::new();
self.process_search_state.is_searching_with_pid = false;
// Close search and reset it
self.process_search_state.search_state.reset();
self.force_update_processes = true;
// Clear current delete list
self.to_delete_process_list = None;
self.dd_err = None;
// Unfreeze.
self.is_frozen = false;
// Reset zoom
self.reset_cpu_zoom();
self.reset_mem_zoom();
self.reset_net_zoom();
// Reset data
self.data_collection.reset();
}
pub fn on_esc(&mut self) {
@ -783,7 +766,7 @@ impl App {
pub fn clear_search(&mut self) {
if let WidgetPosition::ProcessSearch = self.current_widget_selected {
self.force_update_processes = true;
self.process_search_state.search_state = AppSearchState::reset();
self.process_search_state.search_state.reset();
}
}
@ -795,7 +778,7 @@ impl App {
&self.process_search_state.search_state.current_search_query[start_position..],
start_position,
)
.unwrap(); // TODO: [UNWRAP] unwrap in this and walk_back seem sketch
.unwrap();
}
pub fn search_walk_back(&mut self, start_position: usize) {
@ -1544,50 +1527,53 @@ impl App {
fn zoom_out(&mut self) {
match self.current_widget_selected {
WidgetPosition::Cpu => {
let new_time = self.cpu_state.display_time + self.app_config_fields.time_interval;
let new_time =
self.cpu_state.current_display_time + self.app_config_fields.time_interval;
if new_time <= constants::STALE_MAX_MILLISECONDS {
self.cpu_state.display_time = new_time;
self.cpu_state.current_display_time = new_time;
self.cpu_state.force_update = true;
if self.app_config_fields.autohide_time {
self.cpu_state.display_time_instant = Some(Instant::now());
self.cpu_state.autohide_timer = Some(Instant::now());
}
} else if self.cpu_state.display_time != constants::STALE_MAX_MILLISECONDS {
self.cpu_state.display_time = constants::STALE_MAX_MILLISECONDS;
} else if self.cpu_state.current_display_time != constants::STALE_MAX_MILLISECONDS {
self.cpu_state.current_display_time = constants::STALE_MAX_MILLISECONDS;
self.cpu_state.force_update = true;
if self.app_config_fields.autohide_time {
self.cpu_state.display_time_instant = Some(Instant::now());
self.cpu_state.autohide_timer = Some(Instant::now());
}
}
}
WidgetPosition::Mem => {
let new_time = self.mem_state.display_time + self.app_config_fields.time_interval;
let new_time =
self.mem_state.current_display_time + self.app_config_fields.time_interval;
if new_time <= constants::STALE_MAX_MILLISECONDS {
self.mem_state.display_time = new_time;
self.mem_state.current_display_time = new_time;
self.mem_state.force_update = true;
if self.app_config_fields.autohide_time {
self.mem_state.display_time_instant = Some(Instant::now());
self.mem_state.autohide_timer = Some(Instant::now());
}
} else if self.mem_state.display_time != constants::STALE_MAX_MILLISECONDS {
self.mem_state.display_time = constants::STALE_MAX_MILLISECONDS;
} else if self.mem_state.current_display_time != constants::STALE_MAX_MILLISECONDS {
self.mem_state.current_display_time = constants::STALE_MAX_MILLISECONDS;
self.mem_state.force_update = true;
if self.app_config_fields.autohide_time {
self.mem_state.display_time_instant = Some(Instant::now());
self.mem_state.autohide_timer = Some(Instant::now());
}
}
}
WidgetPosition::Network => {
let new_time = self.net_state.display_time + self.app_config_fields.time_interval;
let new_time =
self.net_state.current_display_time + self.app_config_fields.time_interval;
if new_time <= constants::STALE_MAX_MILLISECONDS {
self.net_state.display_time = new_time;
self.net_state.current_display_time = new_time;
self.net_state.force_update = true;
if self.app_config_fields.autohide_time {
self.net_state.display_time_instant = Some(Instant::now());
self.net_state.autohide_timer = Some(Instant::now());
}
} else if self.net_state.display_time != constants::STALE_MAX_MILLISECONDS {
self.net_state.display_time = constants::STALE_MAX_MILLISECONDS;
} else if self.net_state.current_display_time != constants::STALE_MAX_MILLISECONDS {
self.net_state.current_display_time = constants::STALE_MAX_MILLISECONDS;
self.net_state.force_update = true;
if self.app_config_fields.autohide_time {
self.net_state.display_time_instant = Some(Instant::now());
self.net_state.autohide_timer = Some(Instant::now());
}
}
}
@ -1598,50 +1584,53 @@ impl App {
fn zoom_in(&mut self) {
match self.current_widget_selected {
WidgetPosition::Cpu => {
let new_time = self.cpu_state.display_time - self.app_config_fields.time_interval;
let new_time =
self.cpu_state.current_display_time - self.app_config_fields.time_interval;
if new_time >= constants::STALE_MIN_MILLISECONDS {
self.cpu_state.display_time = new_time;
self.cpu_state.current_display_time = new_time;
self.cpu_state.force_update = true;
if self.app_config_fields.autohide_time {
self.cpu_state.display_time_instant = Some(Instant::now());
self.cpu_state.autohide_timer = Some(Instant::now());
}
} else if self.cpu_state.display_time != constants::STALE_MIN_MILLISECONDS {
self.cpu_state.display_time = constants::STALE_MIN_MILLISECONDS;
} else if self.cpu_state.current_display_time != constants::STALE_MIN_MILLISECONDS {
self.cpu_state.current_display_time = constants::STALE_MIN_MILLISECONDS;
self.cpu_state.force_update = true;
if self.app_config_fields.autohide_time {
self.cpu_state.display_time_instant = Some(Instant::now());
self.cpu_state.autohide_timer = Some(Instant::now());
}
}
}
WidgetPosition::Mem => {
let new_time = self.mem_state.display_time - self.app_config_fields.time_interval;
let new_time =
self.mem_state.current_display_time - self.app_config_fields.time_interval;
if new_time >= constants::STALE_MIN_MILLISECONDS {
self.mem_state.display_time = new_time;
self.mem_state.current_display_time = new_time;
self.mem_state.force_update = true;
if self.app_config_fields.autohide_time {
self.mem_state.display_time_instant = Some(Instant::now());
self.mem_state.autohide_timer = Some(Instant::now());
}
} else if self.mem_state.display_time != constants::STALE_MIN_MILLISECONDS {
self.mem_state.display_time = constants::STALE_MIN_MILLISECONDS;
} else if self.mem_state.current_display_time != constants::STALE_MIN_MILLISECONDS {
self.mem_state.current_display_time = constants::STALE_MIN_MILLISECONDS;
self.mem_state.force_update = true;
if self.app_config_fields.autohide_time {
self.mem_state.display_time_instant = Some(Instant::now());
self.mem_state.autohide_timer = Some(Instant::now());
}
}
}
WidgetPosition::Network => {
let new_time = self.net_state.display_time - self.app_config_fields.time_interval;
let new_time =
self.net_state.current_display_time - self.app_config_fields.time_interval;
if new_time >= constants::STALE_MIN_MILLISECONDS {
self.net_state.display_time = new_time;
self.net_state.current_display_time = new_time;
self.net_state.force_update = true;
if self.app_config_fields.autohide_time {
self.net_state.display_time_instant = Some(Instant::now());
self.net_state.autohide_timer = Some(Instant::now());
}
} else if self.net_state.display_time != constants::STALE_MIN_MILLISECONDS {
self.net_state.display_time = constants::STALE_MIN_MILLISECONDS;
} else if self.net_state.current_display_time != constants::STALE_MIN_MILLISECONDS {
self.net_state.current_display_time = constants::STALE_MIN_MILLISECONDS;
self.net_state.force_update = true;
if self.app_config_fields.autohide_time {
self.net_state.display_time_instant = Some(Instant::now());
self.net_state.autohide_timer = Some(Instant::now());
}
}
}
@ -1649,29 +1638,35 @@ impl App {
}
}
fn reset_cpu_zoom(&mut self) {
self.cpu_state.current_display_time = self.app_config_fields.default_time_value;
self.cpu_state.force_update = true;
if self.app_config_fields.autohide_time {
self.cpu_state.autohide_timer = Some(Instant::now());
}
}
fn reset_mem_zoom(&mut self) {
self.mem_state.current_display_time = self.app_config_fields.default_time_value;
self.mem_state.force_update = true;
if self.app_config_fields.autohide_time {
self.mem_state.autohide_timer = Some(Instant::now());
}
}
fn reset_net_zoom(&mut self) {
self.net_state.current_display_time = self.app_config_fields.default_time_value;
self.net_state.force_update = true;
if self.app_config_fields.autohide_time {
self.net_state.autohide_timer = Some(Instant::now());
}
}
fn reset_zoom(&mut self) {
match self.current_widget_selected {
WidgetPosition::Cpu => {
self.cpu_state.display_time = self.app_config_fields.default_time_value;
self.cpu_state.force_update = true;
if self.app_config_fields.autohide_time {
self.cpu_state.display_time_instant = Some(Instant::now());
}
}
WidgetPosition::Mem => {
self.mem_state.display_time = self.app_config_fields.default_time_value;
self.mem_state.force_update = true;
if self.app_config_fields.autohide_time {
self.mem_state.display_time_instant = Some(Instant::now());
}
}
WidgetPosition::Network => {
self.net_state.display_time = self.app_config_fields.default_time_value;
self.net_state.force_update = true;
if self.app_config_fields.autohide_time {
self.net_state.display_time_instant = Some(Instant::now());
}
}
WidgetPosition::Cpu => self.reset_cpu_zoom(),
WidgetPosition::Mem => self.reset_mem_zoom(),
WidgetPosition::Network => self.reset_net_zoom(),
_ => {}
}
}

@ -80,6 +80,20 @@ impl Default for DataCollection {
}
impl DataCollection {
pub fn reset(&mut self) {
self.timed_data_vec = Vec::default();
self.network_harvest = network::NetworkHarvest::default();
self.memory_harvest = mem::MemHarvest::default();
self.swap_harvest = mem::MemHarvest::default();
self.cpu_harvest = cpu::CPUHarvest::default();
self.process_harvest = Vec::default();
self.disk_harvest = Vec::default();
self.io_harvest = disks::IOHarvest::default();
self.io_labels = Vec::default();
self.io_prev = Vec::default();
self.temp_harvest = Vec::default();
}
pub fn set_frozen_time(&mut self) {
self.frozen_instant = Some(self.current_instant);
}

@ -4,6 +4,8 @@ use std::{collections::HashMap, time::Instant};
use sysinfo::{System, SystemExt};
use futures::join;
pub mod cpu;
pub mod disks;
pub mod mem;

@ -232,9 +232,6 @@ pub fn get_sorted_processes_list(
}
*prev_pid_stats = new_pid_stats;
} else {
error!("Unable to properly parse CPU data in Linux.");
error!("Result: {:?}", cpu_calc.err());
}
} else {
let process_hashmap = sys.get_processes();

@ -129,9 +129,7 @@ impl Painter {
}
}
// TODO: [REFACTOR] We should clean this up tbh
// TODO: [FEATURE] Auto-resizing dialog sizes.
#[allow(clippy::cognitive_complexity)]
pub fn draw_data<B: Backend>(
&mut self, terminal: &mut Terminal<B>, app_state: &mut app::App,
) -> error::Result<()> {
@ -139,9 +137,6 @@ impl Painter {
let current_height = terminal_size.height;
let current_width = terminal_size.width;
// TODO: [OPT] we might be able to add an argument s.t. if there is
// no resize AND it's not a data update (or process refresh/search/etc.)
// then just... don't draw again!
if self.height == 0 && self.width == 0 {
self.height = current_height;
self.width = current_width;
@ -336,7 +331,6 @@ impl Painter {
);
}
} else {
// TODO: [TUI] Change this back to a more even 33/33/34 when TUI releases
let vertical_chunks = Layout::default()
.direction(Direction::Vertical)
.margin(1)

@ -1,3 +1,4 @@
use lazy_static::lazy_static;
use std::collections::HashMap;
use tui::style::{Color, Style};

@ -1,3 +1,4 @@
use lazy_static::lazy_static;
use std::borrow::Cow;
use std::cmp::max;
@ -45,31 +46,31 @@ impl CpuGraphWidget for Painter {
let cpu_data: &[ConvertedCpuData] = &app_state.canvas_data.cpu_data;
let display_time_labels = [
format!("{}s", app_state.cpu_state.display_time / 1000),
format!("{}s", app_state.cpu_state.current_display_time / 1000),
"0s".to_string(),
];
let x_axis = if app_state.app_config_fields.hide_time
|| (app_state.app_config_fields.autohide_time
&& app_state.cpu_state.display_time_instant.is_none())
&& app_state.cpu_state.autohide_timer.is_none())
{
Axis::default().bounds([0.0, app_state.cpu_state.display_time as f64])
} else if let Some(time) = app_state.cpu_state.display_time_instant {
Axis::default().bounds([0.0, app_state.cpu_state.current_display_time as f64])
} else if let Some(time) = app_state.cpu_state.autohide_timer {
if std::time::Instant::now().duration_since(time).as_millis()
< AUTOHIDE_TIMEOUT_MILLISECONDS as u128
{
Axis::default()
.bounds([0.0, app_state.cpu_state.display_time as f64])
.bounds([0.0, app_state.cpu_state.current_display_time as f64])
.style(self.colours.graph_style)
.labels_style(self.colours.graph_style)
.labels(&display_time_labels)
} else {
app_state.cpu_state.display_time_instant = None;
Axis::default().bounds([0.0, app_state.cpu_state.display_time as f64])
app_state.cpu_state.autohide_timer = None;
Axis::default().bounds([0.0, app_state.cpu_state.current_display_time as f64])
}
} else {
Axis::default()
.bounds([0.0, app_state.cpu_state.display_time as f64])
.bounds([0.0, app_state.cpu_state.current_display_time as f64])
.style(self.colours.graph_style)
.labels_style(self.colours.graph_style)
.labels(&display_time_labels)

@ -1,5 +1,5 @@
use lazy_static::lazy_static;
use std::cmp::max;
use tui::{
backend::Backend,
layout::{Constraint, Direction, Layout, Rect},

@ -27,30 +27,30 @@ impl MemGraphWidget for Painter {
let swap_data: &[(f64, f64)] = &app_state.canvas_data.swap_data;
let display_time_labels = [
format!("{}s", app_state.mem_state.display_time / 1000),
format!("{}s", app_state.mem_state.current_display_time / 1000),
"0s".to_string(),
];
let x_axis = if app_state.app_config_fields.hide_time
|| (app_state.app_config_fields.autohide_time
&& app_state.mem_state.display_time_instant.is_none())
&& app_state.mem_state.autohide_timer.is_none())
{
Axis::default().bounds([0.0, app_state.mem_state.display_time as f64])
} else if let Some(time) = app_state.mem_state.display_time_instant {
Axis::default().bounds([0.0, app_state.mem_state.current_display_time as f64])
} else if let Some(time) = app_state.mem_state.autohide_timer {
if std::time::Instant::now().duration_since(time).as_millis()
< AUTOHIDE_TIMEOUT_MILLISECONDS as u128
{
Axis::default()
.bounds([0.0, app_state.mem_state.display_time as f64])
.bounds([0.0, app_state.mem_state.current_display_time as f64])
.style(self.colours.graph_style)
.labels_style(self.colours.graph_style)
.labels(&display_time_labels)
} else {
app_state.mem_state.display_time_instant = None;
Axis::default().bounds([0.0, app_state.mem_state.display_time as f64])
app_state.mem_state.autohide_timer = None;
Axis::default().bounds([0.0, app_state.mem_state.current_display_time as f64])
}
} else {
Axis::default()
.bounds([0.0, app_state.mem_state.display_time as f64])
.bounds([0.0, app_state.mem_state.current_display_time as f64])
.style(self.colours.graph_style)
.labels_style(self.colours.graph_style)
.labels(&display_time_labels)

@ -1,3 +1,4 @@
use lazy_static::lazy_static;
use std::cmp::max;
use crate::{
@ -40,30 +41,30 @@ impl NetworkGraphWidget for Painter {
let network_data_tx: &[(f64, f64)] = &app_state.canvas_data.network_data_tx;
let display_time_labels = [
format!("{}s", app_state.net_state.display_time / 1000),
format!("{}s", app_state.net_state.current_display_time / 1000),
"0s".to_string(),
];
let x_axis = if app_state.app_config_fields.hide_time
|| (app_state.app_config_fields.autohide_time
&& app_state.net_state.display_time_instant.is_none())
&& app_state.net_state.autohide_timer.is_none())
{
Axis::default().bounds([0.0, app_state.net_state.display_time as f64])
} else if let Some(time) = app_state.net_state.display_time_instant {
Axis::default().bounds([0.0, app_state.net_state.current_display_time as f64])
} else if let Some(time) = app_state.net_state.autohide_timer {
if std::time::Instant::now().duration_since(time).as_millis()
< AUTOHIDE_TIMEOUT_MILLISECONDS as u128
{
Axis::default()
.bounds([0.0, app_state.net_state.display_time as f64])
.bounds([0.0, app_state.net_state.current_display_time as f64])
.style(self.colours.graph_style)
.labels_style(self.colours.graph_style)
.labels(&display_time_labels)
} else {
app_state.net_state.display_time_instant = None;
Axis::default().bounds([0.0, app_state.net_state.display_time as f64])
app_state.net_state.autohide_timer = None;
Axis::default().bounds([0.0, app_state.net_state.current_display_time as f64])
}
} else {
Axis::default()
.bounds([0.0, app_state.net_state.display_time as f64])
.bounds([0.0, app_state.net_state.current_display_time as f64])
.style(self.colours.graph_style)
.labels_style(self.colours.graph_style)
.labels(&display_time_labels)

@ -1,3 +1,4 @@
use lazy_static::lazy_static;
use std::cmp::max;
use tui::{

@ -1,3 +1,5 @@
use lazy_static::lazy_static;
// How long to store data.
pub const STALE_MAX_MILLISECONDS: u64 = 600 * 1000; // Keep 10 minutes of data.

@ -272,7 +272,7 @@ pub fn get_rx_tx_data_points(
current_data.current_instant
};
// TODO: [REFACTOR] Can we use combine on this, CPU, and MEM?
// TODO: [REFACTOR] Can we use collect on this, CPU, and MEM?
for (time, data) in &current_data.timed_data_vec {
let time_from_start: f64 =
(display_time as f64 - current_time.duration_since(*time).as_millis() as f64).floor();

@ -1,11 +1,5 @@
#![warn(rust_2018_idioms)]
#[macro_use]
extern crate clap;
#[macro_use]
extern crate futures;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
@ -18,6 +12,8 @@ use std::{
time::{Duration, Instant},
};
use clap::*;
use crossterm::{
event::{
poll, read, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent,
@ -109,42 +105,14 @@ fn main() -> error::Result<()> {
let config: Config = create_config(matches.value_of("CONFIG_LOCATION"))?;
let update_rate_in_milliseconds: u64 =
get_update_rate_in_milliseconds(&matches.value_of("RATE_MILLIS"), &config)?;
// Set other settings
let temperature_type = get_temperature_option(&matches, &config)?;
let show_average_cpu = get_avg_cpu_option(&matches, &config);
let use_dot = get_use_dot_option(&matches, &config);
let left_legend = get_use_left_legend_option(&matches, &config);
let use_current_cpu_total = get_use_current_cpu_total_option(&matches, &config);
let current_widget_selected = get_default_widget(&matches, &config);
let show_disabled_data = get_show_disabled_data_option(&matches, &config);
let use_basic_mode = get_use_basic_mode_option(&matches, &config);
let default_time_value = get_default_time_value_option(&matches, &config)?;
let time_interval = get_time_interval_option(&matches, &config)?;
// Create "app" struct, which will control most of the program and store settings/state
let mut app = App::new(
show_average_cpu,
temperature_type,
update_rate_in_milliseconds,
use_dot,
left_legend,
use_current_cpu_total,
current_widget_selected,
show_disabled_data,
use_basic_mode,
default_time_value,
time_interval,
);
let mut app = build_app(&matches, &config)?;
// TODO: [REFACTOR] Change this
enable_app_grouping(&matches, &config, &mut app);
enable_app_case_sensitive(&matches, &config, &mut app);
enable_app_match_whole_word(&matches, &config, &mut app);
enable_app_use_regex(&matches, &config, &mut app);
enable_hide_time(&matches, &config, &mut app);
enable_autohide_time(&matches, &config, &mut app);
// Set up up tui and crossterm
let mut stdout_val = stdout();
@ -166,7 +134,7 @@ fn main() -> error::Result<()> {
let tx = tx.clone();
thread::spawn(move || loop {
thread::sleep(Duration::from_millis(
constants::STALE_MAX_MILLISECONDS as u64 + 5000,
constants::STALE_MAX_MILLISECONDS + 5000,
));
tx.send(BottomEvent::Clean).unwrap();
});
@ -176,8 +144,8 @@ fn main() -> error::Result<()> {
create_event_thread(
tx,
rrx,
use_current_cpu_total,
update_rate_in_milliseconds as u64,
app.app_config_fields.use_current_cpu_total,
app.app_config_fields.update_rate_in_milliseconds,
app.app_config_fields.temperature_type.clone(),
app.app_config_fields.show_average_cpu,
);
@ -213,7 +181,7 @@ fn main() -> error::Result<()> {
// Network
let network_data = convert_network_data_points(
&app.data_collection,
app.net_state.display_time,
app.net_state.current_display_time,
false,
);
app.canvas_data.network_data_rx = network_data.rx;
@ -231,12 +199,12 @@ fn main() -> error::Result<()> {
// Memory
app.canvas_data.mem_data = convert_mem_data_points(
&app.data_collection,
app.mem_state.display_time,
app.mem_state.current_display_time,
false,
);
app.canvas_data.swap_data = convert_swap_data_points(
&app.data_collection,
app.mem_state.display_time,
app.mem_state.current_display_time,
false,
);
let memory_and_swap_labels = convert_mem_labels(&app.data_collection);
@ -254,7 +222,7 @@ fn main() -> error::Result<()> {
// CPU
app.canvas_data.cpu_data = convert_cpu_data_points(
&app.data_collection,
app.cpu_state.display_time,
app.cpu_state.current_display_time,
false,
);
@ -590,7 +558,7 @@ fn handle_force_redraws(app: &mut App) {
if app.cpu_state.force_update {
app.canvas_data.cpu_data = convert_cpu_data_points(
&app.data_collection,
app.cpu_state.display_time,
app.cpu_state.current_display_time,
app.is_frozen,
);
app.cpu_state.force_update = false;
@ -599,12 +567,12 @@ fn handle_force_redraws(app: &mut App) {
if app.mem_state.force_update {
app.canvas_data.mem_data = convert_mem_data_points(
&app.data_collection,
app.mem_state.display_time,
app.mem_state.current_display_time,
app.is_frozen,
);
app.canvas_data.swap_data = convert_swap_data_points(
&app.data_collection,
app.mem_state.display_time,
app.mem_state.current_display_time,
app.is_frozen,
);
app.mem_state.force_update = false;
@ -613,7 +581,7 @@ fn handle_force_redraws(app: &mut App) {
if app.net_state.force_update {
let (rx, tx) = get_rx_tx_data_points(
&app.data_collection,
app.net_state.display_time,
app.net_state.current_display_time,
app.is_frozen,
);
app.canvas_data.network_data_rx = rx;

@ -1,11 +1,17 @@
use serde::Deserialize;
use std::time::Instant;
use crate::{
app::{data_harvester, App, WidgetPosition},
app::{data_harvester, App, AppConfigFields, CpuState, MemState, NetState, WidgetPosition},
constants::*,
utils::error::{self, BottomError},
};
// use layout_manager::*;
// mod layout_manager;
#[derive(Default, Deserialize)]
pub struct Config {
pub flags: Option<ConfigFlags>,
@ -54,10 +60,64 @@ pub struct ConfigColours {
pub graph_color: Option<String>,
}
pub fn get_update_rate_in_milliseconds(
update_rate: &Option<&str>, config: &Config,
pub fn build_app(matches: &clap::ArgMatches<'static>, config: &Config) -> error::Result<App> {
let autohide_time = get_autohide_time(&matches, &config);
let default_time_value = get_default_time_value(&matches, &config)?;
let default_widget = get_default_widget(&matches, &config);
let use_basic_mode = get_use_basic_mode(&matches, &config);
let current_widget_selected = if use_basic_mode {
match default_widget {
WidgetPosition::Cpu => WidgetPosition::BasicCpu,
WidgetPosition::Network => WidgetPosition::BasicNet,
WidgetPosition::Mem => WidgetPosition::BasicMem,
_ => default_widget,
}
} else {
default_widget
};
let previous_basic_table_selected = if default_widget.is_widget_table() {
default_widget
} else {
WidgetPosition::Process
};
let app_config_fields = AppConfigFields {
update_rate_in_milliseconds: get_update_rate_in_milliseconds(matches, config)?,
temperature_type: get_temperature(matches, config)?,
show_average_cpu: get_avg_cpu(matches, config),
use_dot: get_use_dot(matches, config),
left_legend: get_use_left_legend(matches, config),
use_current_cpu_total: get_use_current_cpu_total(matches, config),
show_disabled_data: get_show_disabled_data(matches, config),
use_basic_mode,
default_time_value,
time_interval: get_time_interval(matches, config)?,
hide_time: get_hide_time(matches, config),
autohide_time,
};
let time_now = if autohide_time {
Some(Instant::now())
} else {
None
};
Ok(App::builder()
.app_config_fields(app_config_fields)
.current_widget_selected(current_widget_selected)
.previous_basic_table_selected(previous_basic_table_selected)
.cpu_state(CpuState::init(default_time_value, time_now))
.mem_state(MemState::init(default_time_value, time_now))
.net_state(NetState::init(default_time_value, time_now))
.build())
}
fn get_update_rate_in_milliseconds(
matches: &clap::ArgMatches<'static>, config: &Config,
) -> error::Result<u64> {
let update_rate_in_milliseconds = if let Some(update_rate) = update_rate {
let update_rate_in_milliseconds = if let Some(update_rate) = matches.value_of("RATE_MILLIS") {
update_rate.parse::<u128>()?
} else if let Some(flags) = &config.flags {
if let Some(rate) = flags.rate {
@ -82,7 +142,7 @@ pub fn get_update_rate_in_milliseconds(
Ok(update_rate_in_milliseconds as u64)
}
pub fn get_temperature_option(
fn get_temperature(
matches: &clap::ArgMatches<'static>, config: &Config,
) -> error::Result<data_harvester::temperature::TemperatureType> {
if matches.is_present("FAHRENHEIT") {
@ -109,7 +169,7 @@ pub fn get_temperature_option(
Ok(data_harvester::temperature::TemperatureType::Celsius)
}
pub fn get_avg_cpu_option(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
fn get_avg_cpu(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
if matches.is_present("AVG_CPU") {
return true;
} else if let Some(flags) = &config.flags {
@ -121,7 +181,7 @@ pub fn get_avg_cpu_option(matches: &clap::ArgMatches<'static>, config: &Config)
false
}
pub fn get_use_dot_option(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
fn get_use_dot(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
if matches.is_present("DOT_MARKER") {
return true;
} else if let Some(flags) = &config.flags {
@ -132,7 +192,7 @@ pub fn get_use_dot_option(matches: &clap::ArgMatches<'static>, config: &Config)
false
}
pub fn get_use_left_legend_option(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
fn get_use_left_legend(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
if matches.is_present("LEFT_LEGEND") {
return true;
} else if let Some(flags) = &config.flags {
@ -144,9 +204,7 @@ pub fn get_use_left_legend_option(matches: &clap::ArgMatches<'static>, config: &
false
}
pub fn get_use_current_cpu_total_option(
matches: &clap::ArgMatches<'static>, config: &Config,
) -> bool {
fn get_use_current_cpu_total(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
if matches.is_present("USE_CURR_USAGE") {
return true;
} else if let Some(flags) = &config.flags {
@ -158,7 +216,7 @@ pub fn get_use_current_cpu_total_option(
false
}
pub fn get_show_disabled_data_option(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
fn get_show_disabled_data(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
if matches.is_present("SHOW_DISABLED_DATA") {
return true;
} else if let Some(flags) = &config.flags {
@ -170,7 +228,7 @@ pub fn get_show_disabled_data_option(matches: &clap::ArgMatches<'static>, config
false
}
pub fn get_use_basic_mode_option(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
fn get_use_basic_mode(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
if matches.is_present("BASIC_MODE") {
return true;
} else if let Some(flags) = &config.flags {
@ -182,7 +240,7 @@ pub fn get_use_basic_mode_option(matches: &clap::ArgMatches<'static>, config: &C
false
}
pub fn get_default_time_value_option(
fn get_default_time_value(
matches: &clap::ArgMatches<'static>, config: &Config,
) -> error::Result<u64> {
let default_time = if let Some(default_time_value) = matches.value_of("DEFAULT_TIME_VALUE") {
@ -202,17 +260,16 @@ pub fn get_default_time_value_option(
"Please set your default value to be at least 30000 milliseconds.".to_string(),
));
} else if default_time as u128 > STALE_MAX_MILLISECONDS as u128 {
return Err(BottomError::InvalidArg(
format!("Please set your default value to be at most {} milliseconds.", STALE_MAX_MILLISECONDS),
));
return Err(BottomError::InvalidArg(format!(
"Please set your default value to be at most {} milliseconds.",
STALE_MAX_MILLISECONDS
)));
}
Ok(default_time as u64)
}
pub fn get_time_interval_option(
matches: &clap::ArgMatches<'static>, config: &Config,
) -> error::Result<u64> {
fn get_time_interval(matches: &clap::ArgMatches<'static>, config: &Config) -> error::Result<u64> {
let time_interval = if let Some(time_interval) = matches.value_of("TIME_DELTA") {
time_interval.parse::<u128>()?
} else if let Some(flags) = &config.flags {
@ -230,9 +287,10 @@ pub fn get_time_interval_option(
"Please set your time delta to be at least 1000 milliseconds.".to_string(),
));
} else if time_interval > STALE_MAX_MILLISECONDS as u128 {
return Err(BottomError::InvalidArg(
format!("Please set your time delta to be at most {} milliseconds.", STALE_MAX_MILLISECONDS),
));
return Err(BottomError::InvalidArg(format!(
"Please set your time delta to be at most {} milliseconds.",
STALE_MAX_MILLISECONDS
)));
}
Ok(time_interval as u64)
@ -290,39 +348,34 @@ pub fn enable_app_use_regex(matches: &clap::ArgMatches<'static>, config: &Config
}
}
pub fn enable_hide_time(matches: &clap::ArgMatches<'static>, config: &Config, app: &mut App) {
fn get_hide_time(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
if matches.is_present("HIDE_TIME") {
app.app_config_fields.hide_time = true;
return true;
} else if let Some(flags) = &config.flags {
if let Some(hide_time) = flags.hide_time {
if hide_time {
app.app_config_fields.hide_time = true;
return true;
}
}
}
false
}
pub fn enable_autohide_time(matches: &clap::ArgMatches<'static>, config: &Config, app: &mut App) {
fn get_autohide_time(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
if matches.is_present("AUTOHIDE_TIME") {
app.app_config_fields.autohide_time = true;
let time = Some(std::time::Instant::now());
app.cpu_state.display_time_instant = time;
app.mem_state.display_time_instant = time;
app.net_state.display_time_instant = time;
return true;
} else if let Some(flags) = &config.flags {
if let Some(autohide_time) = flags.autohide_time {
if autohide_time {
app.app_config_fields.autohide_time = true;
let time = Some(std::time::Instant::now());
app.cpu_state.display_time_instant = time;
app.mem_state.display_time_instant = time;
app.net_state.display_time_instant = time;
return true;
}
}
}
false
}
pub fn get_default_widget(matches: &clap::ArgMatches<'static>, config: &Config) -> WidgetPosition {
fn get_default_widget(matches: &clap::ArgMatches<'static>, config: &Config) -> WidgetPosition {
if matches.is_present("CPU_WIDGET") {
return WidgetPosition::Cpu;
} else if matches.is_present("MEM_WIDGET") {

@ -0,0 +1,3 @@
use serde::Deserialize;
use toml::Value;