commit
3ab94edc5d
18
README.md
18
README.md
|
@ -30,7 +30,9 @@ Features of bottom include:
|
||||||
|
|
||||||
- Maximizing of widgets of interest to take up the entire window.
|
- Maximizing of widgets of interest to take up the entire window.
|
||||||
|
|
||||||
- Basic mode
|
- A minimal mode that focuses less on graphs and more on data, similar to [htop](https://hisham.hm/htop/).
|
||||||
|
|
||||||
|
- Zooming in/out to see more/less data.
|
||||||
|
|
||||||
More details about each widget and compatibility can be found [here](./docs/widgets.md).
|
More details about each widget and compatibility can be found [here](./docs/widgets.md).
|
||||||
|
|
||||||
|
@ -118,7 +120,7 @@ Run using `btm`.
|
||||||
|
|
||||||
- `-v`, `--version` displays the version number and exits.
|
- `-v`, `--version` displays the version number and exits.
|
||||||
|
|
||||||
- `-r <RATE>`, `--rate <RATE>` will set the refresh rate in _milliseconds_. Lowest it can go is 250ms, the highest it can go is 2<sup>128</sup> - 1. Defaults to 1000ms, and lower values may take more resources due to more frequent polling of data, and may be less accurate in some circumstances.
|
- `-r <RATE>`, `--rate <RATE>` will set the refresh rate in _milliseconds_. Lowest it can go is 250ms, the highest it can go is 2<sup>64</sup> - 1. Defaults to 1000ms, and lower values may take more resources due to more frequent polling of data, and may be less accurate in some circumstances.
|
||||||
|
|
||||||
- `-l`, `--left_legend` will move external table legends to the left side rather than the right side. Right side is default.
|
- `-l`, `--left_legend` will move external table legends to the left side rather than the right side. Right side is default.
|
||||||
|
|
||||||
|
@ -140,6 +142,10 @@ Run using `btm`.
|
||||||
|
|
||||||
- `-b`, `--basic` will enable basic mode, removing all graphs from the main interface and condensing data.
|
- `-b`, `--basic` will enable basic mode, removing all graphs from the main interface and condensing data.
|
||||||
|
|
||||||
|
- `-t`, `--default_time_value` will set the default time interval graphs will display to (in milliseconds). Lowest is 30 seconds, defaults to 60 seconds.
|
||||||
|
|
||||||
|
- `-d`, `--time_delta` will set the amount each zoom in/out action will change the time interval of a graph (in milliseconds). Lowest is 1 second, defaults to 15 seconds.
|
||||||
|
|
||||||
### Keybindings
|
### Keybindings
|
||||||
|
|
||||||
#### General
|
#### General
|
||||||
|
@ -164,6 +170,12 @@ Run using `btm`.
|
||||||
|
|
||||||
- `Enter` on a widget to maximize the widget.
|
- `Enter` on a widget to maximize the widget.
|
||||||
|
|
||||||
|
- `+` to zoom in (reduce time interval, smallest is 30 seconds).
|
||||||
|
|
||||||
|
- `-` to zoom out (increase time interval, largest is 10 minutes).
|
||||||
|
|
||||||
|
- `=` to reset zoom.
|
||||||
|
|
||||||
#### CPU
|
#### CPU
|
||||||
|
|
||||||
- `/` to allow for enabling/disabling showing certain cores with `Space`.
|
- `/` to allow for enabling/disabling showing certain cores with `Space`.
|
||||||
|
@ -212,6 +224,8 @@ Note that `q` is disabled while in the search widget.
|
||||||
|
|
||||||
- Scrolling with the mouse will scroll through the currently selected list if the widget is a scrollable table.
|
- Scrolling with the mouse will scroll through the currently selected list if the widget is a scrollable table.
|
||||||
|
|
||||||
|
- Scrolling on a graph will zoom in (scroll up) or zoom out (scroll down).
|
||||||
|
|
||||||
## Bugs and Requests
|
## Bugs and Requests
|
||||||
|
|
||||||
Spot an bug? Have an idea? Leave an issue that explains what you want in detail and I'll try to take a look.
|
Spot an bug? Have an idea? Leave an issue that explains what you want in detail and I'll try to take a look.
|
||||||
|
|
355
src/app.rs
355
src/app.rs
|
@ -17,10 +17,12 @@ const MAX_SEARCH_LENGTH: usize = 200;
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum WidgetPosition {
|
pub enum WidgetPosition {
|
||||||
Cpu,
|
Cpu,
|
||||||
|
CpuLegend,
|
||||||
Mem,
|
Mem,
|
||||||
Disk,
|
Disk,
|
||||||
Temp,
|
Temp,
|
||||||
Network,
|
Network,
|
||||||
|
NetworkLegend,
|
||||||
Process,
|
Process,
|
||||||
ProcessSearch,
|
ProcessSearch,
|
||||||
BasicCpu,
|
BasicCpu,
|
||||||
|
@ -34,19 +36,28 @@ impl WidgetPosition {
|
||||||
WidgetPosition::Disk
|
WidgetPosition::Disk
|
||||||
| WidgetPosition::Process
|
| WidgetPosition::Process
|
||||||
| WidgetPosition::ProcessSearch
|
| WidgetPosition::ProcessSearch
|
||||||
| WidgetPosition::Temp => true,
|
| WidgetPosition::Temp
|
||||||
|
| WidgetPosition::CpuLegend => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_widget_graph(self) -> bool {
|
||||||
|
match self {
|
||||||
|
WidgetPosition::Cpu | WidgetPosition::Network | WidgetPosition::Mem => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_pretty_name(self) -> String {
|
pub fn get_pretty_name(self) -> String {
|
||||||
|
use WidgetPosition::*;
|
||||||
match self {
|
match self {
|
||||||
WidgetPosition::Cpu | WidgetPosition::BasicCpu => "CPU",
|
Cpu | BasicCpu | CpuLegend => "CPU",
|
||||||
WidgetPosition::Mem | WidgetPosition::BasicMem => "Memory",
|
Mem | BasicMem => "Memory",
|
||||||
WidgetPosition::Disk => "Disks",
|
Disk => "Disks",
|
||||||
WidgetPosition::Temp => "Temperature",
|
Temp => "Temperature",
|
||||||
WidgetPosition::Network | WidgetPosition::BasicNet => "Network",
|
Network | BasicNet | NetworkLegend => "Network",
|
||||||
WidgetPosition::Process | WidgetPosition::ProcessSearch => "Processes",
|
Process | ProcessSearch => "Processes",
|
||||||
}
|
}
|
||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
@ -198,9 +209,10 @@ impl Default for AppHelpDialogState {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// AppConfigFields is meant to cover basic fields that would normally be set
|
/// AppConfigFields is meant to cover basic fields that would normally be set
|
||||||
/// by config files or launch options. Don't need to be mutable (set and forget).
|
/// by config files or launch options.
|
||||||
|
#[derive(Default)]
|
||||||
pub struct AppConfigFields {
|
pub struct AppConfigFields {
|
||||||
pub update_rate_in_milliseconds: u128,
|
pub update_rate_in_milliseconds: u64,
|
||||||
pub temperature_type: temperature::TemperatureType,
|
pub temperature_type: temperature::TemperatureType,
|
||||||
pub use_dot: bool,
|
pub use_dot: bool,
|
||||||
pub left_legend: bool,
|
pub left_legend: bool,
|
||||||
|
@ -208,23 +220,33 @@ pub struct AppConfigFields {
|
||||||
pub use_current_cpu_total: bool,
|
pub use_current_cpu_total: bool,
|
||||||
pub show_disabled_data: bool,
|
pub show_disabled_data: bool,
|
||||||
pub use_basic_mode: bool,
|
pub use_basic_mode: bool,
|
||||||
|
pub default_time_value: u64,
|
||||||
|
pub time_interval: u64,
|
||||||
|
pub hide_time: bool,
|
||||||
|
pub autohide_time: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Network specific
|
/// Network specific
|
||||||
pub struct NetworkState {
|
pub struct NetState {
|
||||||
pub is_showing_tray: bool,
|
pub is_showing_tray: bool,
|
||||||
pub is_showing_rx: bool,
|
pub is_showing_rx: bool,
|
||||||
pub is_showing_tx: bool,
|
pub is_showing_tx: bool,
|
||||||
pub zoom_level: f64,
|
pub zoom_level: f64,
|
||||||
|
pub display_time: u64,
|
||||||
|
pub force_update: bool,
|
||||||
|
pub display_time_instant: Option<Instant>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for NetworkState {
|
impl Default for NetState {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
NetworkState {
|
NetState {
|
||||||
is_showing_tray: false,
|
is_showing_tray: false,
|
||||||
is_showing_rx: true,
|
is_showing_rx: true,
|
||||||
is_showing_tx: true,
|
is_showing_tx: true,
|
||||||
zoom_level: 100.0,
|
zoom_level: 100.0,
|
||||||
|
display_time: constants::DEFAULT_TIME_MILLISECONDS,
|
||||||
|
force_update: false,
|
||||||
|
display_time_instant: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,6 +256,9 @@ pub struct CpuState {
|
||||||
pub is_showing_tray: bool,
|
pub is_showing_tray: bool,
|
||||||
pub zoom_level: f64,
|
pub zoom_level: f64,
|
||||||
pub core_show_vec: Vec<bool>,
|
pub core_show_vec: Vec<bool>,
|
||||||
|
pub display_time: u64,
|
||||||
|
pub force_update: bool,
|
||||||
|
pub display_time_instant: Option<Instant>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CpuState {
|
impl Default for CpuState {
|
||||||
|
@ -242,6 +267,9 @@ impl Default for CpuState {
|
||||||
is_showing_tray: false,
|
is_showing_tray: false,
|
||||||
zoom_level: 100.0,
|
zoom_level: 100.0,
|
||||||
core_show_vec: Vec::new(),
|
core_show_vec: Vec::new(),
|
||||||
|
display_time: constants::DEFAULT_TIME_MILLISECONDS,
|
||||||
|
force_update: false,
|
||||||
|
display_time_instant: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -252,6 +280,9 @@ pub struct MemState {
|
||||||
pub is_showing_ram: bool,
|
pub is_showing_ram: bool,
|
||||||
pub is_showing_swap: bool,
|
pub is_showing_swap: bool,
|
||||||
pub zoom_level: f64,
|
pub zoom_level: f64,
|
||||||
|
pub display_time: u64,
|
||||||
|
pub force_update: bool,
|
||||||
|
pub display_time_instant: Option<Instant>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MemState {
|
impl Default for MemState {
|
||||||
|
@ -261,6 +292,9 @@ impl Default for MemState {
|
||||||
is_showing_ram: true,
|
is_showing_ram: true,
|
||||||
is_showing_swap: true,
|
is_showing_swap: true,
|
||||||
zoom_level: 100.0,
|
zoom_level: 100.0,
|
||||||
|
display_time: constants::DEFAULT_TIME_MILLISECONDS,
|
||||||
|
force_update: false,
|
||||||
|
display_time_instant: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -268,7 +302,7 @@ impl Default for MemState {
|
||||||
pub struct App {
|
pub struct App {
|
||||||
pub process_sorting_type: processes::ProcessSorting,
|
pub process_sorting_type: processes::ProcessSorting,
|
||||||
pub process_sorting_reverse: bool,
|
pub process_sorting_reverse: bool,
|
||||||
pub update_process_gui: bool,
|
pub force_update_processes: bool,
|
||||||
pub app_scroll_positions: AppScrollState,
|
pub app_scroll_positions: AppScrollState,
|
||||||
pub current_widget_selected: WidgetPosition,
|
pub current_widget_selected: WidgetPosition,
|
||||||
pub previous_basic_table_selected: WidgetPosition,
|
pub previous_basic_table_selected: WidgetPosition,
|
||||||
|
@ -289,21 +323,31 @@ pub struct App {
|
||||||
pub is_resized: bool,
|
pub is_resized: bool,
|
||||||
pub cpu_state: CpuState,
|
pub cpu_state: CpuState,
|
||||||
pub mem_state: MemState,
|
pub mem_state: MemState,
|
||||||
pub net_state: NetworkState,
|
pub net_state: NetState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
// TODO: [REFACTOR] use builder pattern instead.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
show_average_cpu: bool, temperature_type: temperature::TemperatureType,
|
show_average_cpu: bool, temperature_type: temperature::TemperatureType,
|
||||||
update_rate_in_milliseconds: u128, use_dot: bool, left_legend: bool,
|
update_rate_in_milliseconds: u64, use_dot: bool, left_legend: bool,
|
||||||
use_current_cpu_total: bool, current_widget_selected: WidgetPosition,
|
use_current_cpu_total: bool, current_widget_selected: WidgetPosition,
|
||||||
show_disabled_data: bool, use_basic_mode: bool,
|
show_disabled_data: bool, use_basic_mode: bool, default_time_value: u64,
|
||||||
|
time_interval: u64,
|
||||||
) -> App {
|
) -> 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 {
|
App {
|
||||||
process_sorting_type: processes::ProcessSorting::CPU,
|
process_sorting_type: processes::ProcessSorting::CPU,
|
||||||
process_sorting_reverse: true,
|
process_sorting_reverse: true,
|
||||||
update_process_gui: false,
|
force_update_processes: false,
|
||||||
current_widget_selected: if use_basic_mode {
|
current_widget_selected: if use_basic_mode {
|
||||||
match current_widget_selected {
|
match current_widget_selected {
|
||||||
WidgetPosition::Cpu => WidgetPosition::BasicCpu,
|
WidgetPosition::Cpu => WidgetPosition::BasicCpu,
|
||||||
|
@ -341,12 +385,16 @@ impl App {
|
||||||
use_current_cpu_total,
|
use_current_cpu_total,
|
||||||
show_disabled_data,
|
show_disabled_data,
|
||||||
use_basic_mode,
|
use_basic_mode,
|
||||||
|
default_time_value,
|
||||||
|
time_interval,
|
||||||
|
hide_time: false,
|
||||||
|
autohide_time: false,
|
||||||
},
|
},
|
||||||
is_expanded: false,
|
is_expanded: false,
|
||||||
is_resized: false,
|
is_resized: false,
|
||||||
cpu_state: CpuState::default(),
|
cpu_state,
|
||||||
mem_state: MemState::default(),
|
mem_state,
|
||||||
net_state: NetworkState::default(),
|
net_state,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,10 +423,7 @@ impl App {
|
||||||
self.dd_err = None;
|
self.dd_err = None;
|
||||||
} else if self.is_filtering_or_searching() {
|
} else if self.is_filtering_or_searching() {
|
||||||
match self.current_widget_selected {
|
match self.current_widget_selected {
|
||||||
WidgetPosition::Cpu
|
WidgetPosition::Cpu | WidgetPosition::CpuLegend => {
|
||||||
if self.is_expanded && self.app_config_fields.use_basic_mode =>
|
|
||||||
{
|
|
||||||
self.current_widget_selected = WidgetPosition::BasicCpu;
|
|
||||||
self.cpu_state.is_showing_tray = false;
|
self.cpu_state.is_showing_tray = false;
|
||||||
}
|
}
|
||||||
WidgetPosition::Process | WidgetPosition::ProcessSearch => {
|
WidgetPosition::Process | WidgetPosition::ProcessSearch => {
|
||||||
|
@ -387,9 +432,6 @@ impl App {
|
||||||
self.process_search_state.search_state.is_enabled = false;
|
self.process_search_state.search_state.is_enabled = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WidgetPosition::Cpu => {
|
|
||||||
self.cpu_state.is_showing_tray = false;
|
|
||||||
}
|
|
||||||
WidgetPosition::Mem => {
|
WidgetPosition::Mem => {
|
||||||
self.mem_state.is_showing_tray = false;
|
self.mem_state.is_showing_tray = false;
|
||||||
}
|
}
|
||||||
|
@ -401,14 +443,22 @@ impl App {
|
||||||
} else if self.is_expanded {
|
} else if self.is_expanded {
|
||||||
self.is_expanded = false;
|
self.is_expanded = false;
|
||||||
self.is_resized = true;
|
self.is_resized = true;
|
||||||
|
if self.app_config_fields.use_basic_mode {
|
||||||
|
self.current_widget_selected = match self.current_widget_selected {
|
||||||
|
WidgetPosition::Cpu | WidgetPosition::CpuLegend => WidgetPosition::BasicCpu,
|
||||||
|
WidgetPosition::Mem => WidgetPosition::BasicMem,
|
||||||
|
WidgetPosition::Network => WidgetPosition::BasicNet,
|
||||||
|
_ => self.current_widget_selected,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_filtering_or_searching(&self) -> bool {
|
fn is_filtering_or_searching(&self) -> bool {
|
||||||
match self.current_widget_selected {
|
match self.current_widget_selected {
|
||||||
WidgetPosition::Cpu => self.cpu_state.is_showing_tray,
|
WidgetPosition::Cpu | WidgetPosition::CpuLegend => self.cpu_state.is_showing_tray,
|
||||||
WidgetPosition::Mem => self.mem_state.is_showing_tray,
|
// WidgetPosition::Mem => self.mem_state.is_showing_tray,
|
||||||
WidgetPosition::Network => self.net_state.is_showing_tray,
|
// WidgetPosition::Network => self.net_state.is_showing_tray,
|
||||||
WidgetPosition::Process | WidgetPosition::ProcessSearch => {
|
WidgetPosition::Process | WidgetPosition::ProcessSearch => {
|
||||||
self.process_search_state.search_state.is_enabled
|
self.process_search_state.search_state.is_enabled
|
||||||
}
|
}
|
||||||
|
@ -430,7 +480,7 @@ impl App {
|
||||||
if !self.is_in_dialog() {
|
if !self.is_in_dialog() {
|
||||||
if let WidgetPosition::Process = self.current_widget_selected {
|
if let WidgetPosition::Process = self.current_widget_selected {
|
||||||
self.enable_grouping = !(self.enable_grouping);
|
self.enable_grouping = !(self.enable_grouping);
|
||||||
self.update_process_gui = true;
|
self.force_update_processes = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -442,7 +492,7 @@ impl App {
|
||||||
if self.is_grouped() {
|
if self.is_grouped() {
|
||||||
self.search_with_name();
|
self.search_with_name();
|
||||||
} else {
|
} else {
|
||||||
self.update_process_gui = true;
|
self.force_update_processes = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WidgetPosition::ProcessSearch => {
|
WidgetPosition::ProcessSearch => {
|
||||||
|
@ -464,7 +514,7 @@ impl App {
|
||||||
|
|
||||||
pub fn on_space(&mut self) {
|
pub fn on_space(&mut self) {
|
||||||
match self.current_widget_selected {
|
match self.current_widget_selected {
|
||||||
WidgetPosition::Cpu => {
|
WidgetPosition::CpuLegend => {
|
||||||
let curr_posn = self
|
let curr_posn = self
|
||||||
.app_scroll_positions
|
.app_scroll_positions
|
||||||
.cpu_scroll_state
|
.cpu_scroll_state
|
||||||
|
@ -496,8 +546,9 @@ impl App {
|
||||||
self.search_with_name();
|
self.search_with_name();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WidgetPosition::Cpu => {
|
WidgetPosition::Cpu | WidgetPosition::CpuLegend => {
|
||||||
self.cpu_state.is_showing_tray = true;
|
self.cpu_state.is_showing_tray = true;
|
||||||
|
self.current_widget_selected = WidgetPosition::CpuLegend
|
||||||
}
|
}
|
||||||
// WidgetPosition::Mem => {
|
// WidgetPosition::Mem => {
|
||||||
// self.mem_state.is_showing_tray = true;
|
// self.mem_state.is_showing_tray = true;
|
||||||
|
@ -525,14 +576,14 @@ impl App {
|
||||||
pub fn search_with_pid(&mut self) {
|
pub fn search_with_pid(&mut self) {
|
||||||
if !self.is_in_dialog() && self.is_searching() {
|
if !self.is_in_dialog() && self.is_searching() {
|
||||||
self.process_search_state.is_searching_with_pid = true;
|
self.process_search_state.is_searching_with_pid = true;
|
||||||
self.update_process_gui = true;
|
self.force_update_processes = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn search_with_name(&mut self) {
|
pub fn search_with_name(&mut self) {
|
||||||
if !self.is_in_dialog() && self.is_searching() {
|
if !self.is_in_dialog() && self.is_searching() {
|
||||||
self.process_search_state.is_searching_with_pid = false;
|
self.process_search_state.is_searching_with_pid = false;
|
||||||
self.update_process_gui = true;
|
self.force_update_processes = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -543,19 +594,19 @@ impl App {
|
||||||
pub fn toggle_ignore_case(&mut self) {
|
pub fn toggle_ignore_case(&mut self) {
|
||||||
self.process_search_state.search_toggle_ignore_case();
|
self.process_search_state.search_toggle_ignore_case();
|
||||||
self.update_regex();
|
self.update_regex();
|
||||||
self.update_process_gui = true;
|
self.force_update_processes = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toggle_search_whole_word(&mut self) {
|
pub fn toggle_search_whole_word(&mut self) {
|
||||||
self.process_search_state.search_toggle_whole_word();
|
self.process_search_state.search_toggle_whole_word();
|
||||||
self.update_regex();
|
self.update_regex();
|
||||||
self.update_process_gui = true;
|
self.force_update_processes = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toggle_search_regex(&mut self) {
|
pub fn toggle_search_regex(&mut self) {
|
||||||
self.process_search_state.search_toggle_regex();
|
self.process_search_state.search_toggle_regex();
|
||||||
self.update_regex();
|
self.update_regex();
|
||||||
self.update_process_gui = true;
|
self.force_update_processes = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_regex(&mut self) {
|
pub fn update_regex(&mut self) {
|
||||||
|
@ -650,6 +701,15 @@ impl App {
|
||||||
self.is_resized = true;
|
self.is_resized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.app_config_fields.use_basic_mode {
|
||||||
|
self.current_widget_selected = match self.current_widget_selected {
|
||||||
|
WidgetPosition::BasicCpu => WidgetPosition::Cpu,
|
||||||
|
WidgetPosition::BasicMem => WidgetPosition::Mem,
|
||||||
|
WidgetPosition::BasicNet => WidgetPosition::Network,
|
||||||
|
_ => self.current_widget_selected,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -680,7 +740,7 @@ impl App {
|
||||||
);
|
);
|
||||||
|
|
||||||
self.update_regex();
|
self.update_regex();
|
||||||
self.update_process_gui = true;
|
self.force_update_processes = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -697,7 +757,7 @@ impl App {
|
||||||
|
|
||||||
pub fn clear_search(&mut self) {
|
pub fn clear_search(&mut self) {
|
||||||
if let WidgetPosition::ProcessSearch = self.current_widget_selected {
|
if let WidgetPosition::ProcessSearch = self.current_widget_selected {
|
||||||
self.update_process_gui = true;
|
self.force_update_processes = true;
|
||||||
self.process_search_state.search_state = AppSearchState::reset();
|
self.process_search_state.search_state = AppSearchState::reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -749,7 +809,7 @@ impl App {
|
||||||
self.process_search_state.search_state.cursor_direction = CursorDirection::LEFT;
|
self.process_search_state.search_state.cursor_direction = CursorDirection::LEFT;
|
||||||
|
|
||||||
self.update_regex();
|
self.update_regex();
|
||||||
self.update_process_gui = true;
|
self.force_update_processes = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -911,7 +971,7 @@ impl App {
|
||||||
if current_key_press_inst
|
if current_key_press_inst
|
||||||
.duration_since(self.last_key_press)
|
.duration_since(self.last_key_press)
|
||||||
.as_millis()
|
.as_millis()
|
||||||
> constants::MAX_KEY_TIMEOUT_IN_MILLISECONDS
|
> constants::MAX_KEY_TIMEOUT_IN_MILLISECONDS as u128
|
||||||
{
|
{
|
||||||
self.reset_multi_tap_keys();
|
self.reset_multi_tap_keys();
|
||||||
}
|
}
|
||||||
|
@ -944,7 +1004,7 @@ impl App {
|
||||||
UnicodeWidthChar::width(caught_char).unwrap_or(0);
|
UnicodeWidthChar::width(caught_char).unwrap_or(0);
|
||||||
|
|
||||||
self.update_regex();
|
self.update_regex();
|
||||||
self.update_process_gui = true;
|
self.force_update_processes = true;
|
||||||
self.process_search_state.search_state.cursor_direction =
|
self.process_search_state.search_state.cursor_direction =
|
||||||
CursorDirection::RIGHT;
|
CursorDirection::RIGHT;
|
||||||
}
|
}
|
||||||
|
@ -993,6 +1053,9 @@ impl App {
|
||||||
'j' => self.increment_position_count(),
|
'j' => self.increment_position_count(),
|
||||||
'f' => {
|
'f' => {
|
||||||
self.is_frozen = !self.is_frozen;
|
self.is_frozen = !self.is_frozen;
|
||||||
|
if self.is_frozen {
|
||||||
|
self.data_collection.set_frozen_time();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
'c' => {
|
'c' => {
|
||||||
match self.process_sorting_type {
|
match self.process_sorting_type {
|
||||||
|
@ -1004,7 +1067,7 @@ impl App {
|
||||||
self.process_sorting_reverse = true;
|
self.process_sorting_reverse = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.update_process_gui = true;
|
self.force_update_processes = true;
|
||||||
self.app_scroll_positions
|
self.app_scroll_positions
|
||||||
.process_scroll_state
|
.process_scroll_state
|
||||||
.current_scroll_position = 0;
|
.current_scroll_position = 0;
|
||||||
|
@ -1019,7 +1082,7 @@ impl App {
|
||||||
self.process_sorting_reverse = true;
|
self.process_sorting_reverse = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.update_process_gui = true;
|
self.force_update_processes = true;
|
||||||
self.app_scroll_positions
|
self.app_scroll_positions
|
||||||
.process_scroll_state
|
.process_scroll_state
|
||||||
.current_scroll_position = 0;
|
.current_scroll_position = 0;
|
||||||
|
@ -1036,7 +1099,7 @@ impl App {
|
||||||
self.process_sorting_reverse = false;
|
self.process_sorting_reverse = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.update_process_gui = true;
|
self.force_update_processes = true;
|
||||||
self.app_scroll_positions
|
self.app_scroll_positions
|
||||||
.process_scroll_state
|
.process_scroll_state
|
||||||
.current_scroll_position = 0;
|
.current_scroll_position = 0;
|
||||||
|
@ -1052,7 +1115,7 @@ impl App {
|
||||||
self.process_sorting_reverse = false;
|
self.process_sorting_reverse = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.update_process_gui = true;
|
self.force_update_processes = true;
|
||||||
self.app_scroll_positions
|
self.app_scroll_positions
|
||||||
.process_scroll_state
|
.process_scroll_state
|
||||||
.current_scroll_position = 0;
|
.current_scroll_position = 0;
|
||||||
|
@ -1065,6 +1128,9 @@ impl App {
|
||||||
'K' => self.move_widget_selection_up(),
|
'K' => self.move_widget_selection_up(),
|
||||||
'J' => self.move_widget_selection_down(),
|
'J' => self.move_widget_selection_down(),
|
||||||
' ' => self.on_space(),
|
' ' => self.on_space(),
|
||||||
|
'+' => self.zoom_in(),
|
||||||
|
'-' => self.zoom_out(),
|
||||||
|
'=' => self.reset_zoom(),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1124,6 +1190,12 @@ impl App {
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
self.current_widget_selected = match self.current_widget_selected {
|
self.current_widget_selected = match self.current_widget_selected {
|
||||||
|
WidgetPosition::Cpu if self.app_config_fields.left_legend => {
|
||||||
|
WidgetPosition::CpuLegend
|
||||||
|
}
|
||||||
|
WidgetPosition::CpuLegend if !self.app_config_fields.left_legend => {
|
||||||
|
WidgetPosition::Cpu
|
||||||
|
}
|
||||||
WidgetPosition::Process => WidgetPosition::Network,
|
WidgetPosition::Process => WidgetPosition::Network,
|
||||||
WidgetPosition::ProcessSearch => WidgetPosition::Network,
|
WidgetPosition::ProcessSearch => WidgetPosition::Network,
|
||||||
WidgetPosition::Disk => WidgetPosition::Mem,
|
WidgetPosition::Disk => WidgetPosition::Mem,
|
||||||
|
@ -1131,6 +1203,16 @@ impl App {
|
||||||
_ => self.current_widget_selected,
|
_ => self.current_widget_selected,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
} else if self.is_expanded {
|
||||||
|
self.current_widget_selected = match self.current_widget_selected {
|
||||||
|
WidgetPosition::Cpu if self.app_config_fields.left_legend => {
|
||||||
|
WidgetPosition::CpuLegend
|
||||||
|
}
|
||||||
|
WidgetPosition::CpuLegend if !self.app_config_fields.left_legend => {
|
||||||
|
WidgetPosition::Cpu
|
||||||
|
}
|
||||||
|
_ => self.current_widget_selected,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.reset_multi_tap_keys();
|
self.reset_multi_tap_keys();
|
||||||
|
@ -1149,11 +1231,27 @@ impl App {
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
self.current_widget_selected = match self.current_widget_selected {
|
self.current_widget_selected = match self.current_widget_selected {
|
||||||
|
WidgetPosition::Cpu if !self.app_config_fields.left_legend => {
|
||||||
|
WidgetPosition::CpuLegend
|
||||||
|
}
|
||||||
|
WidgetPosition::CpuLegend if self.app_config_fields.left_legend => {
|
||||||
|
WidgetPosition::Cpu
|
||||||
|
}
|
||||||
WidgetPosition::Mem => WidgetPosition::Temp,
|
WidgetPosition::Mem => WidgetPosition::Temp,
|
||||||
WidgetPosition::Network => WidgetPosition::Process,
|
WidgetPosition::Network => WidgetPosition::Process,
|
||||||
_ => self.current_widget_selected,
|
_ => self.current_widget_selected,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
} else if self.is_expanded {
|
||||||
|
self.current_widget_selected = match self.current_widget_selected {
|
||||||
|
WidgetPosition::Cpu if !self.app_config_fields.left_legend => {
|
||||||
|
WidgetPosition::CpuLegend
|
||||||
|
}
|
||||||
|
WidgetPosition::CpuLegend if self.app_config_fields.left_legend => {
|
||||||
|
WidgetPosition::Cpu
|
||||||
|
}
|
||||||
|
_ => self.current_widget_selected,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.reset_multi_tap_keys();
|
self.reset_multi_tap_keys();
|
||||||
|
@ -1213,7 +1311,7 @@ impl App {
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
self.current_widget_selected = match self.current_widget_selected {
|
self.current_widget_selected = match self.current_widget_selected {
|
||||||
WidgetPosition::Cpu => WidgetPosition::Mem,
|
WidgetPosition::Cpu | WidgetPosition::CpuLegend => WidgetPosition::Mem,
|
||||||
WidgetPosition::Mem => WidgetPosition::Network,
|
WidgetPosition::Mem => WidgetPosition::Network,
|
||||||
WidgetPosition::Temp => WidgetPosition::Disk,
|
WidgetPosition::Temp => WidgetPosition::Disk,
|
||||||
WidgetPosition::Disk => WidgetPosition::Process,
|
WidgetPosition::Disk => WidgetPosition::Process,
|
||||||
|
@ -1261,7 +1359,7 @@ impl App {
|
||||||
.disk_scroll_state
|
.disk_scroll_state
|
||||||
.current_scroll_position = 0
|
.current_scroll_position = 0
|
||||||
}
|
}
|
||||||
WidgetPosition::Cpu => {
|
WidgetPosition::CpuLegend => {
|
||||||
self.app_scroll_positions
|
self.app_scroll_positions
|
||||||
.cpu_scroll_state
|
.cpu_scroll_state
|
||||||
.current_scroll_position = 0
|
.current_scroll_position = 0
|
||||||
|
@ -1294,7 +1392,7 @@ impl App {
|
||||||
.disk_scroll_state
|
.disk_scroll_state
|
||||||
.current_scroll_position = self.canvas_data.disk_data.len() as u64 - 1
|
.current_scroll_position = self.canvas_data.disk_data.len() as u64 - 1
|
||||||
}
|
}
|
||||||
WidgetPosition::Cpu => {
|
WidgetPosition::CpuLegend => {
|
||||||
self.app_scroll_positions
|
self.app_scroll_positions
|
||||||
.cpu_scroll_state
|
.cpu_scroll_state
|
||||||
.current_scroll_position = self.canvas_data.cpu_data.len() as u64 - 1;
|
.current_scroll_position = self.canvas_data.cpu_data.len() as u64 - 1;
|
||||||
|
@ -1312,7 +1410,7 @@ impl App {
|
||||||
WidgetPosition::Process => self.change_process_position(-1),
|
WidgetPosition::Process => self.change_process_position(-1),
|
||||||
WidgetPosition::Temp => self.change_temp_position(-1),
|
WidgetPosition::Temp => self.change_temp_position(-1),
|
||||||
WidgetPosition::Disk => self.change_disk_position(-1),
|
WidgetPosition::Disk => self.change_disk_position(-1),
|
||||||
WidgetPosition::Cpu => self.change_cpu_table_position(-1), // TODO: [PO?] Temporary, may change if we add scaling
|
WidgetPosition::CpuLegend => self.change_cpu_table_position(-1),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
self.app_scroll_positions.scroll_direction = ScrollDirection::UP;
|
self.app_scroll_positions.scroll_direction = ScrollDirection::UP;
|
||||||
|
@ -1326,7 +1424,7 @@ impl App {
|
||||||
WidgetPosition::Process => self.change_process_position(1),
|
WidgetPosition::Process => self.change_process_position(1),
|
||||||
WidgetPosition::Temp => self.change_temp_position(1),
|
WidgetPosition::Temp => self.change_temp_position(1),
|
||||||
WidgetPosition::Disk => self.change_disk_position(1),
|
WidgetPosition::Disk => self.change_disk_position(1),
|
||||||
WidgetPosition::Cpu => self.change_cpu_table_position(1), // TODO: [PO?] Temporary, may change if we add scaling
|
WidgetPosition::CpuLegend => self.change_cpu_table_position(1),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
self.app_scroll_positions.scroll_direction = ScrollDirection::DOWN;
|
self.app_scroll_positions.scroll_direction = ScrollDirection::DOWN;
|
||||||
|
@ -1395,4 +1493,155 @@ impl App {
|
||||||
.current_scroll_position = (current_posn as i64 + num_to_change_by) as u64;
|
.current_scroll_position = (current_posn as i64 + num_to_change_by) as u64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn handle_scroll_up(&mut self) {
|
||||||
|
if self.current_widget_selected.is_widget_graph() {
|
||||||
|
self.zoom_in();
|
||||||
|
} else if self.current_widget_selected.is_widget_table() {
|
||||||
|
self.decrement_position_count();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_scroll_down(&mut self) {
|
||||||
|
if self.current_widget_selected.is_widget_graph() {
|
||||||
|
self.zoom_out();
|
||||||
|
} else if self.current_widget_selected.is_widget_table() {
|
||||||
|
self.increment_position_count();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
if new_time <= constants::STALE_MAX_MILLISECONDS {
|
||||||
|
self.cpu_state.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());
|
||||||
|
}
|
||||||
|
} else if self.cpu_state.display_time != constants::STALE_MAX_MILLISECONDS {
|
||||||
|
self.cpu_state.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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WidgetPosition::Mem => {
|
||||||
|
let new_time = self.mem_state.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.force_update = true;
|
||||||
|
if self.app_config_fields.autohide_time {
|
||||||
|
self.mem_state.display_time_instant = Some(Instant::now());
|
||||||
|
}
|
||||||
|
} else if self.mem_state.display_time != constants::STALE_MAX_MILLISECONDS {
|
||||||
|
self.mem_state.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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WidgetPosition::Network => {
|
||||||
|
let new_time = self.net_state.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.force_update = true;
|
||||||
|
if self.app_config_fields.autohide_time {
|
||||||
|
self.net_state.display_time_instant = Some(Instant::now());
|
||||||
|
}
|
||||||
|
} else if self.net_state.display_time != constants::STALE_MAX_MILLISECONDS {
|
||||||
|
self.net_state.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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
if new_time >= constants::STALE_MIN_MILLISECONDS {
|
||||||
|
self.cpu_state.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());
|
||||||
|
}
|
||||||
|
} else if self.cpu_state.display_time != constants::STALE_MIN_MILLISECONDS {
|
||||||
|
self.cpu_state.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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WidgetPosition::Mem => {
|
||||||
|
let new_time = self.mem_state.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.force_update = true;
|
||||||
|
if self.app_config_fields.autohide_time {
|
||||||
|
self.mem_state.display_time_instant = Some(Instant::now());
|
||||||
|
}
|
||||||
|
} else if self.mem_state.display_time != constants::STALE_MIN_MILLISECONDS {
|
||||||
|
self.mem_state.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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WidgetPosition::Network => {
|
||||||
|
let new_time = self.net_state.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.force_update = true;
|
||||||
|
if self.app_config_fields.autohide_time {
|
||||||
|
self.net_state.display_time_instant = Some(Instant::now());
|
||||||
|
}
|
||||||
|
} else if self.net_state.display_time != constants::STALE_MIN_MILLISECONDS {
|
||||||
|
self.net_state.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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ pub struct TimedData {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DataCollection {
|
pub struct DataCollection {
|
||||||
pub current_instant: Instant,
|
pub current_instant: Instant,
|
||||||
|
pub frozen_instant: Option<Instant>,
|
||||||
pub timed_data_vec: Vec<(Instant, TimedData)>,
|
pub timed_data_vec: Vec<(Instant, TimedData)>,
|
||||||
pub network_harvest: network::NetworkHarvest,
|
pub network_harvest: network::NetworkHarvest,
|
||||||
pub memory_harvest: mem::MemHarvest,
|
pub memory_harvest: mem::MemHarvest,
|
||||||
|
@ -62,6 +63,7 @@ impl Default for DataCollection {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
DataCollection {
|
DataCollection {
|
||||||
current_instant: Instant::now(),
|
current_instant: Instant::now(),
|
||||||
|
frozen_instant: None,
|
||||||
timed_data_vec: Vec::default(),
|
timed_data_vec: Vec::default(),
|
||||||
network_harvest: network::NetworkHarvest::default(),
|
network_harvest: network::NetworkHarvest::default(),
|
||||||
memory_harvest: mem::MemHarvest::default(),
|
memory_harvest: mem::MemHarvest::default(),
|
||||||
|
@ -78,12 +80,16 @@ impl Default for DataCollection {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DataCollection {
|
impl DataCollection {
|
||||||
pub fn clean_data(&mut self, max_time_millis: u128) {
|
pub fn set_frozen_time(&mut self) {
|
||||||
|
self.frozen_instant = Some(self.current_instant);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clean_data(&mut self, max_time_millis: u64) {
|
||||||
let current_time = Instant::now();
|
let current_time = Instant::now();
|
||||||
|
|
||||||
let mut remove_index = 0;
|
let mut remove_index = 0;
|
||||||
for entry in &self.timed_data_vec {
|
for entry in &self.timed_data_vec {
|
||||||
if current_time.duration_since(entry.0).as_millis() >= max_time_millis {
|
if current_time.duration_since(entry.0).as_millis() >= max_time_millis as u128 {
|
||||||
remove_index += 1;
|
remove_index += 1;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -35,12 +35,12 @@ pub struct DisplayableData {
|
||||||
pub network_data_tx: Vec<(f64, f64)>,
|
pub network_data_tx: Vec<(f64, f64)>,
|
||||||
pub disk_data: Vec<Vec<String>>,
|
pub disk_data: Vec<Vec<String>>,
|
||||||
pub temp_sensor_data: Vec<Vec<String>>,
|
pub temp_sensor_data: Vec<Vec<String>>,
|
||||||
|
// Not the final value
|
||||||
pub process_data: HashMap<u32, ProcessHarvest>,
|
pub process_data: HashMap<u32, ProcessHarvest>,
|
||||||
// Not the final value
|
// Not the final value
|
||||||
pub grouped_process_data: Vec<ConvertedProcessData>,
|
pub grouped_process_data: Vec<ConvertedProcessData>,
|
||||||
// Not the final value
|
|
||||||
pub finalized_process_data: Vec<ConvertedProcessData>,
|
|
||||||
// What's actually displayed
|
// What's actually displayed
|
||||||
|
pub finalized_process_data: Vec<ConvertedProcessData>,
|
||||||
pub mem_label: String,
|
pub mem_label: String,
|
||||||
pub swap_label: String,
|
pub swap_label: String,
|
||||||
pub mem_data: Vec<(f64, f64)>,
|
pub mem_data: Vec<(f64, f64)>,
|
||||||
|
@ -72,44 +72,47 @@ pub struct Painter {
|
||||||
impl Painter {
|
impl Painter {
|
||||||
/// Must be run once before drawing, but after setting colours.
|
/// Must be run once before drawing, but after setting colours.
|
||||||
/// This is to set some remaining styles and text.
|
/// This is to set some remaining styles and text.
|
||||||
/// This bypasses some logic checks (size > 2, for example) but this
|
|
||||||
/// assumes that you, the programmer, are sane and do not do stupid things.
|
|
||||||
/// RIGHT?
|
|
||||||
pub fn initialize(&mut self) {
|
pub fn initialize(&mut self) {
|
||||||
self.is_mac_os = cfg!(target_os = "macos");
|
self.is_mac_os = cfg!(target_os = "macos");
|
||||||
|
|
||||||
self.styled_general_help_text.push(Text::Styled(
|
if GENERAL_HELP_TEXT.len() > 1 {
|
||||||
GENERAL_HELP_TEXT[0].into(),
|
self.styled_general_help_text.push(Text::Styled(
|
||||||
self.colours.table_header_style,
|
GENERAL_HELP_TEXT[0].into(),
|
||||||
));
|
self.colours.table_header_style,
|
||||||
self.styled_general_help_text.extend(
|
));
|
||||||
GENERAL_HELP_TEXT[1..]
|
self.styled_general_help_text.extend(
|
||||||
.iter()
|
GENERAL_HELP_TEXT[1..]
|
||||||
.map(|&text| Text::Styled(text.into(), self.colours.text_style))
|
.iter()
|
||||||
.collect::<Vec<_>>(),
|
.map(|&text| Text::Styled(text.into(), self.colours.text_style))
|
||||||
);
|
.collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
self.styled_process_help_text.push(Text::Styled(
|
if PROCESS_HELP_TEXT.len() > 1 {
|
||||||
PROCESS_HELP_TEXT[0].into(),
|
self.styled_process_help_text.push(Text::Styled(
|
||||||
self.colours.table_header_style,
|
PROCESS_HELP_TEXT[0].into(),
|
||||||
));
|
self.colours.table_header_style,
|
||||||
self.styled_process_help_text.extend(
|
));
|
||||||
PROCESS_HELP_TEXT[1..]
|
self.styled_process_help_text.extend(
|
||||||
.iter()
|
PROCESS_HELP_TEXT[1..]
|
||||||
.map(|&text| Text::Styled(text.into(), self.colours.text_style))
|
.iter()
|
||||||
.collect::<Vec<_>>(),
|
.map(|&text| Text::Styled(text.into(), self.colours.text_style))
|
||||||
);
|
.collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
self.styled_search_help_text.push(Text::Styled(
|
if SEARCH_HELP_TEXT.len() > 1 {
|
||||||
SEARCH_HELP_TEXT[0].into(),
|
self.styled_search_help_text.push(Text::Styled(
|
||||||
self.colours.table_header_style,
|
SEARCH_HELP_TEXT[0].into(),
|
||||||
));
|
self.colours.table_header_style,
|
||||||
self.styled_search_help_text.extend(
|
));
|
||||||
SEARCH_HELP_TEXT[1..]
|
self.styled_search_help_text.extend(
|
||||||
.iter()
|
SEARCH_HELP_TEXT[1..]
|
||||||
.map(|&text| Text::Styled(text.into(), self.colours.text_style))
|
.iter()
|
||||||
.collect::<Vec<_>>(),
|
.map(|&text| Text::Styled(text.into(), self.colours.text_style))
|
||||||
);
|
.collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_specific_table<B: Backend>(
|
pub fn draw_specific_table<B: Backend>(
|
||||||
|
@ -235,7 +238,7 @@ impl Painter {
|
||||||
.constraints([Constraint::Percentage(100)].as_ref())
|
.constraints([Constraint::Percentage(100)].as_ref())
|
||||||
.split(f.size());
|
.split(f.size());
|
||||||
match &app_state.current_widget_selected {
|
match &app_state.current_widget_selected {
|
||||||
WidgetPosition::Cpu | WidgetPosition::BasicCpu => {
|
WidgetPosition::Cpu | WidgetPosition::BasicCpu | WidgetPosition::CpuLegend => {
|
||||||
let cpu_chunk = Layout::default()
|
let cpu_chunk = Layout::default()
|
||||||
.direction(Direction::Horizontal)
|
.direction(Direction::Horizontal)
|
||||||
.margin(0)
|
.margin(0)
|
||||||
|
@ -260,11 +263,11 @@ impl Painter {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
self.draw_cpu_graph(&mut f, &app_state, cpu_chunk[graph_index]);
|
self.draw_cpu_graph(&mut f, app_state, cpu_chunk[graph_index]);
|
||||||
self.draw_cpu_legend(&mut f, app_state, cpu_chunk[legend_index]);
|
self.draw_cpu_legend(&mut f, app_state, cpu_chunk[legend_index]);
|
||||||
}
|
}
|
||||||
WidgetPosition::Mem | WidgetPosition::BasicMem => {
|
WidgetPosition::Mem | WidgetPosition::BasicMem => {
|
||||||
self.draw_memory_graph(&mut f, &app_state, rect[0]);
|
self.draw_memory_graph(&mut f, app_state, rect[0]);
|
||||||
}
|
}
|
||||||
WidgetPosition::Disk => {
|
WidgetPosition::Disk => {
|
||||||
self.draw_disk_table(&mut f, app_state, rect[0], true);
|
self.draw_disk_table(&mut f, app_state, rect[0], true);
|
||||||
|
@ -272,8 +275,10 @@ impl Painter {
|
||||||
WidgetPosition::Temp => {
|
WidgetPosition::Temp => {
|
||||||
self.draw_temp_table(&mut f, app_state, rect[0], true);
|
self.draw_temp_table(&mut f, app_state, rect[0], true);
|
||||||
}
|
}
|
||||||
WidgetPosition::Network | WidgetPosition::BasicNet => {
|
WidgetPosition::Network
|
||||||
self.draw_network_graph(&mut f, &app_state, rect[0]);
|
| WidgetPosition::BasicNet
|
||||||
|
| WidgetPosition::NetworkLegend => {
|
||||||
|
self.draw_network_graph(&mut f, app_state, rect[0]);
|
||||||
}
|
}
|
||||||
WidgetPosition::Process | WidgetPosition::ProcessSearch => {
|
WidgetPosition::Process | WidgetPosition::ProcessSearch => {
|
||||||
self.draw_process_and_search(&mut f, app_state, rect[0], true);
|
self.draw_process_and_search(&mut f, app_state, rect[0], true);
|
||||||
|
@ -406,10 +411,10 @@ impl Painter {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
self.draw_cpu_graph(&mut f, &app_state, cpu_chunk[graph_index]);
|
self.draw_cpu_graph(&mut f, app_state, cpu_chunk[graph_index]);
|
||||||
self.draw_cpu_legend(&mut f, app_state, cpu_chunk[legend_index]);
|
self.draw_cpu_legend(&mut f, app_state, cpu_chunk[legend_index]);
|
||||||
self.draw_memory_graph(&mut f, &app_state, middle_chunks[0]);
|
self.draw_memory_graph(&mut f, app_state, middle_chunks[0]);
|
||||||
self.draw_network_graph(&mut f, &app_state, network_chunk[0]);
|
self.draw_network_graph(&mut f, app_state, network_chunk[0]);
|
||||||
self.draw_network_labels(&mut f, app_state, network_chunk[1]);
|
self.draw_network_labels(&mut f, app_state, network_chunk[1]);
|
||||||
self.draw_temp_table(&mut f, app_state, middle_divided_chunk_2[0], true);
|
self.draw_temp_table(&mut f, app_state, middle_divided_chunk_2[0], true);
|
||||||
self.draw_disk_table(&mut f, app_state, middle_divided_chunk_2[1], true);
|
self.draw_disk_table(&mut f, app_state, middle_divided_chunk_2[1], true);
|
||||||
|
|
|
@ -17,7 +17,7 @@ use tui::{
|
||||||
widgets::{Axis, Block, Borders, Chart, Dataset, Marker, Row, Table, Widget},
|
widgets::{Axis, Block, Borders, Chart, Dataset, Marker, Row, Table, Widget},
|
||||||
};
|
};
|
||||||
|
|
||||||
const CPU_SELECT_LEGEND_HEADER: [&str; 2] = ["CPU", "Show (Space)"];
|
const CPU_SELECT_LEGEND_HEADER: [&str; 2] = ["CPU", "Show"];
|
||||||
const CPU_LEGEND_HEADER: [&str; 2] = ["CPU", "Use%"];
|
const CPU_LEGEND_HEADER: [&str; 2] = ["CPU", "Use%"];
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref CPU_LEGEND_HEADER_LENS: Vec<usize> = CPU_LEGEND_HEADER
|
static ref CPU_LEGEND_HEADER_LENS: Vec<usize> = CPU_LEGEND_HEADER
|
||||||
|
@ -31,18 +31,50 @@ lazy_static! {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait CpuGraphWidget {
|
pub trait CpuGraphWidget {
|
||||||
fn draw_cpu_graph<B: Backend>(&self, f: &mut Frame<'_, B>, app_state: &App, draw_loc: Rect);
|
fn draw_cpu_graph<B: Backend>(&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect);
|
||||||
fn draw_cpu_legend<B: Backend>(
|
fn draw_cpu_legend<B: Backend>(
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CpuGraphWidget for Painter {
|
impl CpuGraphWidget for Painter {
|
||||||
fn draw_cpu_graph<B: Backend>(&self, f: &mut Frame<'_, B>, app_state: &App, draw_loc: Rect) {
|
fn draw_cpu_graph<B: Backend>(
|
||||||
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||||
|
) {
|
||||||
let cpu_data: &[ConvertedCpuData] = &app_state.canvas_data.cpu_data;
|
let cpu_data: &[ConvertedCpuData] = &app_state.canvas_data.cpu_data;
|
||||||
|
|
||||||
// CPU usage graph
|
let display_time_labels = [
|
||||||
let x_axis: Axis<'_, String> = Axis::default().bounds([0.0, TIME_STARTS_FROM as f64]);
|
format!("{}s", app_state.cpu_state.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())
|
||||||
|
{
|
||||||
|
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 {
|
||||||
|
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])
|
||||||
|
.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])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Axis::default()
|
||||||
|
.bounds([0.0, app_state.cpu_state.display_time as f64])
|
||||||
|
.style(self.colours.graph_style)
|
||||||
|
.labels_style(self.colours.graph_style)
|
||||||
|
.labels(&display_time_labels)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Note this is offset as otherwise the 0 value is not drawn!
|
||||||
let y_axis = Axis::default()
|
let y_axis = Axis::default()
|
||||||
.style(self.colours.graph_style)
|
.style(self.colours.graph_style)
|
||||||
.labels_style(self.colours.graph_style)
|
.labels_style(self.colours.graph_style)
|
||||||
|
@ -92,22 +124,22 @@ impl CpuGraphWidget for Painter {
|
||||||
" CPU ".to_string()
|
" CPU ".to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let border_style = match app_state.current_widget_selected {
|
||||||
|
WidgetPosition::Cpu => self.colours.highlighted_border_style,
|
||||||
|
_ => self.colours.border_style,
|
||||||
|
};
|
||||||
|
|
||||||
Chart::default()
|
Chart::default()
|
||||||
.block(
|
.block(
|
||||||
Block::default()
|
Block::default()
|
||||||
.title(&title)
|
.title(&title)
|
||||||
.title_style(if app_state.is_expanded {
|
.title_style(if app_state.is_expanded {
|
||||||
self.colours.highlighted_border_style
|
border_style
|
||||||
} else {
|
} else {
|
||||||
self.colours.widget_title_style
|
self.colours.widget_title_style
|
||||||
})
|
})
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.border_style(match app_state.current_widget_selected {
|
.border_style(border_style),
|
||||||
WidgetPosition::Cpu | WidgetPosition::BasicCpu => {
|
|
||||||
self.colours.highlighted_border_style
|
|
||||||
}
|
|
||||||
_ => self.colours.border_style,
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
.x_axis(x_axis)
|
.x_axis(x_axis)
|
||||||
.y_axis(y_axis)
|
.y_axis(y_axis)
|
||||||
|
@ -167,7 +199,7 @@ impl CpuGraphWidget for Painter {
|
||||||
Row::StyledData(
|
Row::StyledData(
|
||||||
cpu_string_row.iter(),
|
cpu_string_row.iter(),
|
||||||
match app_state.current_widget_selected {
|
match app_state.current_widget_selected {
|
||||||
WidgetPosition::Cpu => {
|
WidgetPosition::CpuLegend => {
|
||||||
if itx as u64
|
if itx as u64
|
||||||
== app_state
|
== app_state
|
||||||
.app_scroll_positions
|
.app_scroll_positions
|
||||||
|
@ -225,6 +257,11 @@ impl CpuGraphWidget for Painter {
|
||||||
"".to_string()
|
"".to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let title_and_border_style = match app_state.current_widget_selected {
|
||||||
|
WidgetPosition::CpuLegend => self.colours.highlighted_border_style,
|
||||||
|
_ => self.colours.border_style,
|
||||||
|
};
|
||||||
|
|
||||||
// Draw
|
// Draw
|
||||||
Table::new(
|
Table::new(
|
||||||
if app_state.cpu_state.is_showing_tray {
|
if app_state.cpu_state.is_showing_tray {
|
||||||
|
@ -238,19 +275,9 @@ impl CpuGraphWidget for Painter {
|
||||||
.block(
|
.block(
|
||||||
Block::default()
|
Block::default()
|
||||||
.title(&title)
|
.title(&title)
|
||||||
.title_style(if app_state.is_expanded {
|
.title_style(title_and_border_style)
|
||||||
self.colours.highlighted_border_style
|
|
||||||
} else {
|
|
||||||
match app_state.current_widget_selected {
|
|
||||||
WidgetPosition::Cpu => self.colours.highlighted_border_style,
|
|
||||||
_ => self.colours.border_style,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.border_style(match app_state.current_widget_selected {
|
.border_style(title_and_border_style),
|
||||||
WidgetPosition::Cpu => self.colours.highlighted_border_style,
|
|
||||||
_ => self.colours.border_style,
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
.header_style(self.colours.table_header_style)
|
.header_style(self.colours.table_header_style)
|
||||||
.widths(
|
.widths(
|
||||||
|
|
|
@ -14,15 +14,47 @@ use tui::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait MemGraphWidget {
|
pub trait MemGraphWidget {
|
||||||
fn draw_memory_graph<B: Backend>(&self, f: &mut Frame<'_, B>, app_state: &App, draw_loc: Rect);
|
fn draw_memory_graph<B: Backend>(
|
||||||
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MemGraphWidget for Painter {
|
impl MemGraphWidget for Painter {
|
||||||
fn draw_memory_graph<B: Backend>(&self, f: &mut Frame<'_, B>, app_state: &App, draw_loc: Rect) {
|
fn draw_memory_graph<B: Backend>(
|
||||||
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||||
|
) {
|
||||||
let mem_data: &[(f64, f64)] = &app_state.canvas_data.mem_data;
|
let mem_data: &[(f64, f64)] = &app_state.canvas_data.mem_data;
|
||||||
let swap_data: &[(f64, f64)] = &app_state.canvas_data.swap_data;
|
let swap_data: &[(f64, f64)] = &app_state.canvas_data.swap_data;
|
||||||
|
|
||||||
let x_axis: Axis<'_, String> = Axis::default().bounds([0.0, TIME_STARTS_FROM as f64]);
|
let display_time_labels = [
|
||||||
|
format!("{}s", app_state.mem_state.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())
|
||||||
|
{
|
||||||
|
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 {
|
||||||
|
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])
|
||||||
|
.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])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Axis::default()
|
||||||
|
.bounds([0.0, app_state.mem_state.display_time as f64])
|
||||||
|
.style(self.colours.graph_style)
|
||||||
|
.labels_style(self.colours.graph_style)
|
||||||
|
.labels(&display_time_labels)
|
||||||
|
};
|
||||||
|
|
||||||
// Offset as the zero value isn't drawn otherwise...
|
// Offset as the zero value isn't drawn otherwise...
|
||||||
let y_axis: Axis<'_, &str> = Axis::default()
|
let y_axis: Axis<'_, &str> = Axis::default()
|
||||||
|
@ -79,9 +111,7 @@ impl MemGraphWidget for Painter {
|
||||||
})
|
})
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.border_style(match app_state.current_widget_selected {
|
.border_style(match app_state.current_widget_selected {
|
||||||
WidgetPosition::Mem | WidgetPosition::BasicMem => {
|
WidgetPosition::Mem => self.colours.highlighted_border_style,
|
||||||
self.colours.highlighted_border_style
|
|
||||||
}
|
|
||||||
_ => self.colours.border_style,
|
_ => self.colours.border_style,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
|
@ -23,7 +23,9 @@ lazy_static! {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait NetworkGraphWidget {
|
pub trait NetworkGraphWidget {
|
||||||
fn draw_network_graph<B: Backend>(&self, f: &mut Frame<'_, B>, app_state: &App, draw_loc: Rect);
|
fn draw_network_graph<B: Backend>(
|
||||||
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||||
|
);
|
||||||
|
|
||||||
fn draw_network_labels<B: Backend>(
|
fn draw_network_labels<B: Backend>(
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||||
|
@ -32,12 +34,42 @@ pub trait NetworkGraphWidget {
|
||||||
|
|
||||||
impl NetworkGraphWidget for Painter {
|
impl NetworkGraphWidget for Painter {
|
||||||
fn draw_network_graph<B: Backend>(
|
fn draw_network_graph<B: Backend>(
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &App, draw_loc: Rect,
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||||
) {
|
) {
|
||||||
let network_data_rx: &[(f64, f64)] = &app_state.canvas_data.network_data_rx;
|
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 network_data_tx: &[(f64, f64)] = &app_state.canvas_data.network_data_tx;
|
||||||
|
|
||||||
let x_axis: Axis<'_, String> = Axis::default().bounds([0.0, 60_000.0]);
|
let display_time_labels = [
|
||||||
|
format!("{}s", app_state.net_state.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())
|
||||||
|
{
|
||||||
|
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 {
|
||||||
|
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])
|
||||||
|
.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])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Axis::default()
|
||||||
|
.bounds([0.0, app_state.net_state.display_time as f64])
|
||||||
|
.style(self.colours.graph_style)
|
||||||
|
.labels_style(self.colours.graph_style)
|
||||||
|
.labels(&display_time_labels)
|
||||||
|
};
|
||||||
|
|
||||||
|
// 0 is offset.
|
||||||
let y_axis: Axis<'_, &str> = Axis::default()
|
let y_axis: Axis<'_, &str> = Axis::default()
|
||||||
.style(self.colours.graph_style)
|
.style(self.colours.graph_style)
|
||||||
.labels_style(self.colours.graph_style)
|
.labels_style(self.colours.graph_style)
|
||||||
|
@ -71,9 +103,7 @@ impl NetworkGraphWidget for Painter {
|
||||||
})
|
})
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.border_style(match app_state.current_widget_selected {
|
.border_style(match app_state.current_widget_selected {
|
||||||
WidgetPosition::Network | WidgetPosition::BasicNet => {
|
WidgetPosition::Network => self.colours.highlighted_border_style,
|
||||||
self.colours.highlighted_border_style
|
|
||||||
}
|
|
||||||
_ => self.colours.border_style,
|
_ => self.colours.border_style,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
// How long to store data.
|
// How long to store data.
|
||||||
pub const STALE_MAX_MILLISECONDS: u128 = 60 * 1000;
|
pub const STALE_MAX_MILLISECONDS: u64 = 600 * 1000; // Keep 10 minutes of data.
|
||||||
pub const TIME_STARTS_FROM: u64 = 60 * 1000;
|
|
||||||
|
// How much data is SHOWN
|
||||||
|
pub const DEFAULT_TIME_MILLISECONDS: u64 = 60 * 1000; // Defaults to 1 min.
|
||||||
|
pub const STALE_MIN_MILLISECONDS: u64 = 30 * 1000; // Lowest is 30 seconds
|
||||||
|
pub const TIME_CHANGE_MILLISECONDS: u64 = 15 * 1000; // How much to increment each time
|
||||||
|
pub const AUTOHIDE_TIMEOUT_MILLISECONDS: u64 = 5000; // 5 seconds to autohide
|
||||||
|
|
||||||
pub const TICK_RATE_IN_MILLISECONDS: u64 = 200;
|
pub const TICK_RATE_IN_MILLISECONDS: u64 = 200;
|
||||||
// How fast the screen refreshes
|
// How fast the screen refreshes
|
||||||
pub const DEFAULT_REFRESH_RATE_IN_MILLISECONDS: u128 = 1000;
|
pub const DEFAULT_REFRESH_RATE_IN_MILLISECONDS: u64 = 1000;
|
||||||
pub const MAX_KEY_TIMEOUT_IN_MILLISECONDS: u128 = 1000;
|
pub const MAX_KEY_TIMEOUT_IN_MILLISECONDS: u64 = 1000;
|
||||||
// Number of colours to generate for the CPU chart/table
|
// Number of colours to generate for the CPU chart/table
|
||||||
pub const NUM_COLOURS: i32 = 256;
|
pub const NUM_COLOURS: i32 = 256;
|
||||||
|
|
||||||
|
@ -25,7 +31,7 @@ lazy_static! {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Help text
|
// Help text
|
||||||
pub const GENERAL_HELP_TEXT: [&str; 15] = [
|
pub const GENERAL_HELP_TEXT: [&str; 18] = [
|
||||||
"General Keybindings\n\n",
|
"General Keybindings\n\n",
|
||||||
"q, Ctrl-c Quit bottom\n",
|
"q, Ctrl-c Quit bottom\n",
|
||||||
"Esc Close filters, dialog boxes, etc.\n",
|
"Esc Close filters, dialog boxes, etc.\n",
|
||||||
|
@ -41,6 +47,9 @@ pub const GENERAL_HELP_TEXT: [&str; 15] = [
|
||||||
"G Skip to the last entry of a list\n",
|
"G Skip to the last entry of a list\n",
|
||||||
"Enter Maximize the currently selected widget\n",
|
"Enter Maximize the currently selected widget\n",
|
||||||
"/ Filter out graph lines (only CPU at the moment)\n",
|
"/ Filter out graph lines (only CPU at the moment)\n",
|
||||||
|
"+ Zoom in (decrease time range)\n",
|
||||||
|
"- Zoom out (increase time range)\n",
|
||||||
|
"= Reset zoom\n",
|
||||||
];
|
];
|
||||||
|
|
||||||
pub const PROCESS_HELP_TEXT: [&str; 8] = [
|
pub const PROCESS_HELP_TEXT: [&str; 8] = [
|
||||||
|
@ -85,15 +94,34 @@ pub const DEFAULT_CONFIG_CONTENT: &str = r##"
|
||||||
# is also set here.
|
# is also set here.
|
||||||
[flags]
|
[flags]
|
||||||
|
|
||||||
|
# Whether to display an average cpu entry.
|
||||||
#avg_cpu = true
|
#avg_cpu = true
|
||||||
|
|
||||||
|
# Whether to use dot markers rather than braille.
|
||||||
#dot_marker = false
|
#dot_marker = false
|
||||||
|
|
||||||
|
# The update rate of the application.
|
||||||
#rate = 1000
|
#rate = 1000
|
||||||
|
|
||||||
|
# Whether to put the CPU legend to the left.
|
||||||
#left_legend = false
|
#left_legend = false
|
||||||
|
|
||||||
|
# Whether to set CPU% on a process to be based on the total CPU or just current usage.
|
||||||
#current_usage = false
|
#current_usage = false
|
||||||
|
|
||||||
|
# Whether to group processes with the same name together by default.
|
||||||
#group_processes = false
|
#group_processes = false
|
||||||
|
|
||||||
|
# Whether to make process searching case sensitive by default.
|
||||||
#case_sensitive = false
|
#case_sensitive = false
|
||||||
|
|
||||||
|
# Whether to make process searching look for matching the entire word by default.
|
||||||
#whole_word = true
|
#whole_word = true
|
||||||
|
|
||||||
|
# Whether to make process searching use regex by default.
|
||||||
#regex = true
|
#regex = true
|
||||||
|
|
||||||
|
# Whether to show CPU entries in the legend when they are hidden.
|
||||||
#show_disabled_data = true
|
#show_disabled_data = true
|
||||||
|
|
||||||
# Defaults to Celsius. Temperature is one of:
|
# Defaults to Celsius. Temperature is one of:
|
||||||
|
@ -112,6 +140,11 @@ pub const DEFAULT_CONFIG_CONTENT: &str = r##"
|
||||||
#default_widget = "network_default"
|
#default_widget = "network_default"
|
||||||
#default_widget = "process_default"
|
#default_widget = "process_default"
|
||||||
|
|
||||||
|
# The default time interval (in milliseconds).
|
||||||
|
#default_time_value = 60000
|
||||||
|
|
||||||
|
# The time delta on each zoom in/out action (in milliseconds).
|
||||||
|
#time_delta = 15000
|
||||||
|
|
||||||
# These are all the components that support custom theming. Currently, it only
|
# These are all the components that support custom theming. Currently, it only
|
||||||
# supports taking in a string representing a hex colour. Note that colour support
|
# supports taking in a string representing a hex colour. Note that colour support
|
||||||
|
@ -127,7 +160,7 @@ pub const DEFAULT_CONFIG_CONTENT: &str = r##"
|
||||||
# Represents the colour of the label each widget has.
|
# Represents the colour of the label each widget has.
|
||||||
#widget_title_color="#cc241d"
|
#widget_title_color="#cc241d"
|
||||||
|
|
||||||
# Represents the average CPU color
|
# Represents the average CPU color.
|
||||||
#avg_cpu_color="#d3869b"
|
#avg_cpu_color="#d3869b"
|
||||||
|
|
||||||
# Represents the colour the core will use in the CPU legend and graph.
|
# Represents the colour the core will use in the CPU legend and graph.
|
||||||
|
|
|
@ -3,22 +3,21 @@
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use constants::*;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
data_farmer,
|
data_farmer,
|
||||||
data_harvester::{self, processes::ProcessHarvest},
|
data_harvester::{self, processes::ProcessHarvest},
|
||||||
App,
|
App,
|
||||||
},
|
},
|
||||||
constants,
|
|
||||||
utils::gen_util::{get_exact_byte_values, get_simple_byte_values},
|
utils::gen_util::{get_exact_byte_values, get_simple_byte_values},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type Point = (f64, f64);
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct ConvertedNetworkData {
|
pub struct ConvertedNetworkData {
|
||||||
pub rx: Vec<(f64, f64)>,
|
pub rx: Vec<Point>,
|
||||||
pub tx: Vec<(f64, f64)>,
|
pub tx: Vec<Point>,
|
||||||
pub rx_display: String,
|
pub rx_display: String,
|
||||||
pub tx_display: String,
|
pub tx_display: String,
|
||||||
pub total_rx_display: String,
|
pub total_rx_display: String,
|
||||||
|
@ -38,7 +37,7 @@ pub struct ConvertedProcessData {
|
||||||
pub struct ConvertedCpuData {
|
pub struct ConvertedCpuData {
|
||||||
pub cpu_name: String,
|
pub cpu_name: String,
|
||||||
/// Tuple is time, value
|
/// Tuple is time, value
|
||||||
pub cpu_data: Vec<(f64, f64)>,
|
pub cpu_data: Vec<Point>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convert_temp_row(app: &App) -> Vec<Vec<String>> {
|
pub fn convert_temp_row(app: &App) -> Vec<Vec<String>> {
|
||||||
|
@ -103,16 +102,24 @@ pub fn convert_disk_row(current_data: &data_farmer::DataCollection) -> Vec<Vec<S
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convert_cpu_data_points(
|
pub fn convert_cpu_data_points(
|
||||||
show_avg_cpu: bool, current_data: &data_farmer::DataCollection,
|
show_avg_cpu: bool, current_data: &data_farmer::DataCollection, display_time: u64,
|
||||||
|
is_frozen: bool,
|
||||||
) -> Vec<ConvertedCpuData> {
|
) -> Vec<ConvertedCpuData> {
|
||||||
let mut cpu_data_vector: Vec<ConvertedCpuData> = Vec::new();
|
let mut cpu_data_vector: Vec<ConvertedCpuData> = Vec::new();
|
||||||
let current_time = current_data.current_instant;
|
let current_time = if is_frozen {
|
||||||
|
if let Some(frozen_instant) = current_data.frozen_instant {
|
||||||
|
frozen_instant
|
||||||
|
} else {
|
||||||
|
current_data.current_instant
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
current_data.current_instant
|
||||||
|
};
|
||||||
let cpu_listing_offset = if show_avg_cpu { 0 } else { 1 };
|
let cpu_listing_offset = if show_avg_cpu { 0 } else { 1 };
|
||||||
|
|
||||||
for (time, data) in ¤t_data.timed_data_vec {
|
for (time, data) in ¤t_data.timed_data_vec {
|
||||||
let time_from_start: f64 = (TIME_STARTS_FROM as f64
|
let time_from_start: f64 =
|
||||||
- current_time.duration_since(*time).as_millis() as f64)
|
(display_time as f64 - current_time.duration_since(*time).as_millis() as f64).floor();
|
||||||
.floor();
|
|
||||||
|
|
||||||
for (itx, cpu) in data.cpu_data.iter().enumerate() {
|
for (itx, cpu) in data.cpu_data.iter().enumerate() {
|
||||||
if !show_avg_cpu && itx == 0 {
|
if !show_avg_cpu && itx == 0 {
|
||||||
|
@ -139,19 +146,32 @@ pub fn convert_cpu_data_points(
|
||||||
.cpu_data
|
.cpu_data
|
||||||
.push((time_from_start, cpu.0));
|
.push((time_from_start, cpu.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *time == current_time {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu_data_vector
|
cpu_data_vector
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convert_mem_data_points(current_data: &data_farmer::DataCollection) -> Vec<(f64, f64)> {
|
pub fn convert_mem_data_points(
|
||||||
let mut result: Vec<(f64, f64)> = Vec::new();
|
current_data: &data_farmer::DataCollection, display_time: u64, is_frozen: bool,
|
||||||
let current_time = current_data.current_instant;
|
) -> Vec<Point> {
|
||||||
|
let mut result: Vec<Point> = Vec::new();
|
||||||
|
let current_time = if is_frozen {
|
||||||
|
if let Some(frozen_instant) = current_data.frozen_instant {
|
||||||
|
frozen_instant
|
||||||
|
} else {
|
||||||
|
current_data.current_instant
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
current_data.current_instant
|
||||||
|
};
|
||||||
|
|
||||||
for (time, data) in ¤t_data.timed_data_vec {
|
for (time, data) in ¤t_data.timed_data_vec {
|
||||||
let time_from_start: f64 = (TIME_STARTS_FROM as f64
|
let time_from_start: f64 =
|
||||||
- current_time.duration_since(*time).as_millis() as f64)
|
(display_time as f64 - current_time.duration_since(*time).as_millis() as f64).floor();
|
||||||
.floor();
|
|
||||||
|
|
||||||
//Insert joiner points
|
//Insert joiner points
|
||||||
for &(joiner_offset, joiner_val) in &data.mem_data.1 {
|
for &(joiner_offset, joiner_val) in &data.mem_data.1 {
|
||||||
|
@ -160,19 +180,32 @@ pub fn convert_mem_data_points(current_data: &data_farmer::DataCollection) -> Ve
|
||||||
}
|
}
|
||||||
|
|
||||||
result.push((time_from_start, data.mem_data.0));
|
result.push((time_from_start, data.mem_data.0));
|
||||||
|
|
||||||
|
if *time == current_time {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convert_swap_data_points(current_data: &data_farmer::DataCollection) -> Vec<(f64, f64)> {
|
pub fn convert_swap_data_points(
|
||||||
let mut result: Vec<(f64, f64)> = Vec::new();
|
current_data: &data_farmer::DataCollection, display_time: u64, is_frozen: bool,
|
||||||
let current_time = current_data.current_instant;
|
) -> Vec<Point> {
|
||||||
|
let mut result: Vec<Point> = Vec::new();
|
||||||
|
let current_time = if is_frozen {
|
||||||
|
if let Some(frozen_instant) = current_data.frozen_instant {
|
||||||
|
frozen_instant
|
||||||
|
} else {
|
||||||
|
current_data.current_instant
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
current_data.current_instant
|
||||||
|
};
|
||||||
|
|
||||||
for (time, data) in ¤t_data.timed_data_vec {
|
for (time, data) in ¤t_data.timed_data_vec {
|
||||||
let time_from_start: f64 = (TIME_STARTS_FROM as f64
|
let time_from_start: f64 =
|
||||||
- current_time.duration_since(*time).as_millis() as f64)
|
(display_time as f64 - current_time.duration_since(*time).as_millis() as f64).floor();
|
||||||
.floor();
|
|
||||||
|
|
||||||
//Insert joiner points
|
//Insert joiner points
|
||||||
for &(joiner_offset, joiner_val) in &data.swap_data.1 {
|
for &(joiner_offset, joiner_val) in &data.swap_data.1 {
|
||||||
|
@ -181,6 +214,10 @@ pub fn convert_swap_data_points(current_data: &data_farmer::DataCollection) -> V
|
||||||
}
|
}
|
||||||
|
|
||||||
result.push((time_from_start, data.swap_data.0));
|
result.push((time_from_start, data.swap_data.0));
|
||||||
|
|
||||||
|
if *time == current_time {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
|
@ -222,17 +259,26 @@ pub fn convert_mem_labels(current_data: &data_farmer::DataCollection) -> (String
|
||||||
(mem_label, swap_label)
|
(mem_label, swap_label)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convert_network_data_points(
|
pub fn get_rx_tx_data_points(
|
||||||
current_data: &data_farmer::DataCollection,
|
current_data: &data_farmer::DataCollection, display_time: u64, is_frozen: bool,
|
||||||
) -> ConvertedNetworkData {
|
) -> (Vec<Point>, Vec<Point>) {
|
||||||
let mut rx: Vec<(f64, f64)> = Vec::new();
|
let mut rx: Vec<Point> = Vec::new();
|
||||||
let mut tx: Vec<(f64, f64)> = Vec::new();
|
let mut tx: Vec<Point> = Vec::new();
|
||||||
|
|
||||||
let current_time = current_data.current_instant;
|
let current_time = if is_frozen {
|
||||||
|
if let Some(frozen_instant) = current_data.frozen_instant {
|
||||||
|
frozen_instant
|
||||||
|
} else {
|
||||||
|
current_data.current_instant
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
current_data.current_instant
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: [REFACTOR] Can we use combine on this, CPU, and MEM?
|
||||||
for (time, data) in ¤t_data.timed_data_vec {
|
for (time, data) in ¤t_data.timed_data_vec {
|
||||||
let time_from_start: f64 = (TIME_STARTS_FROM as f64
|
let time_from_start: f64 =
|
||||||
- current_time.duration_since(*time).as_millis() as f64)
|
(display_time as f64 - current_time.duration_since(*time).as_millis() as f64).floor();
|
||||||
.floor();
|
|
||||||
|
|
||||||
//Insert joiner points
|
//Insert joiner points
|
||||||
for &(joiner_offset, joiner_val) in &data.rx_data.1 {
|
for &(joiner_offset, joiner_val) in &data.rx_data.1 {
|
||||||
|
@ -247,8 +293,20 @@ pub fn convert_network_data_points(
|
||||||
|
|
||||||
rx.push((time_from_start, data.rx_data.0));
|
rx.push((time_from_start, data.rx_data.0));
|
||||||
tx.push((time_from_start, data.tx_data.0));
|
tx.push((time_from_start, data.tx_data.0));
|
||||||
|
|
||||||
|
if *time == current_time {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(rx, tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn convert_network_data_points(
|
||||||
|
current_data: &data_farmer::DataCollection, display_time: u64, is_frozen: bool,
|
||||||
|
) -> ConvertedNetworkData {
|
||||||
|
let (rx, tx) = get_rx_tx_data_points(current_data, display_time, is_frozen);
|
||||||
|
|
||||||
let total_rx_converted_result: (f64, String);
|
let total_rx_converted_result: (f64, String);
|
||||||
let rx_converted_result: (f64, String);
|
let rx_converted_result: (f64, String);
|
||||||
let total_tx_converted_result: (f64, String);
|
let total_tx_converted_result: (f64, String);
|
||||||
|
|
95
src/main.rs
95
src/main.rs
|
@ -85,8 +85,12 @@ fn get_matches() -> clap::ArgMatches<'static> {
|
||||||
(@arg CASE_SENSITIVE: -S --case_sensitive "Match case when searching by default.")
|
(@arg CASE_SENSITIVE: -S --case_sensitive "Match case when searching by default.")
|
||||||
(@arg WHOLE_WORD: -W --whole_word "Match whole word when searching by default.")
|
(@arg WHOLE_WORD: -W --whole_word "Match whole word when searching by default.")
|
||||||
(@arg REGEX_DEFAULT: -R --regex "Use regex in searching by default.")
|
(@arg REGEX_DEFAULT: -R --regex "Use regex in searching by default.")
|
||||||
(@arg SHOW_DISABLED_DATA: -s --show_disabled_data "Show disabled data entries.")
|
(@arg SHOW_DISABLED_DATA: -s --show_disabled_data "Show disabled data entries.")
|
||||||
(@group DEFAULT_WIDGET =>
|
(@arg DEFAULT_TIME_VALUE: -t --default_time_value +takes_value "Default time value for graphs in milliseconds; minimum is 30s, defaults to 60s.")
|
||||||
|
(@arg TIME_DELTA: -d --time_delta +takes_value "The amount changed upon zooming in/out in milliseconds; minimum is 1s, defaults to 15s.")
|
||||||
|
(@arg HIDE_TIME: --hide_time "Completely hide the time scaling")
|
||||||
|
(@arg AUTOHIDE_TIME: --autohide_time "Automatically hide the time scaling in graphs after being shown for a brief moment when zoomed in/out. If time is disabled via --hide_time then this will have no effect.")
|
||||||
|
(@group DEFAULT_WIDGET =>
|
||||||
(@arg CPU_WIDGET: --cpu_default "Selects the CPU widget to be selected by default.")
|
(@arg CPU_WIDGET: --cpu_default "Selects the CPU widget to be selected by default.")
|
||||||
(@arg MEM_WIDGET: --memory_default "Selects the memory widget to be selected by default.")
|
(@arg MEM_WIDGET: --memory_default "Selects the memory widget to be selected by default.")
|
||||||
(@arg DISK_WIDGET: --disk_default "Selects the disk widget to be selected by default.")
|
(@arg DISK_WIDGET: --disk_default "Selects the disk widget to be selected by default.")
|
||||||
|
@ -105,7 +109,7 @@ fn main() -> error::Result<()> {
|
||||||
|
|
||||||
let config: Config = create_config(matches.value_of("CONFIG_LOCATION"))?;
|
let config: Config = create_config(matches.value_of("CONFIG_LOCATION"))?;
|
||||||
|
|
||||||
let update_rate_in_milliseconds: u128 =
|
let update_rate_in_milliseconds: u64 =
|
||||||
get_update_rate_in_milliseconds(&matches.value_of("RATE_MILLIS"), &config)?;
|
get_update_rate_in_milliseconds(&matches.value_of("RATE_MILLIS"), &config)?;
|
||||||
|
|
||||||
// Set other settings
|
// Set other settings
|
||||||
|
@ -117,6 +121,8 @@ fn main() -> error::Result<()> {
|
||||||
let current_widget_selected = get_default_widget(&matches, &config);
|
let current_widget_selected = get_default_widget(&matches, &config);
|
||||||
let show_disabled_data = get_show_disabled_data_option(&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 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
|
// Create "app" struct, which will control most of the program and store settings/state
|
||||||
let mut app = App::new(
|
let mut app = App::new(
|
||||||
|
@ -129,12 +135,16 @@ fn main() -> error::Result<()> {
|
||||||
current_widget_selected,
|
current_widget_selected,
|
||||||
show_disabled_data,
|
show_disabled_data,
|
||||||
use_basic_mode,
|
use_basic_mode,
|
||||||
|
default_time_value,
|
||||||
|
time_interval,
|
||||||
);
|
);
|
||||||
|
|
||||||
enable_app_grouping(&matches, &config, &mut app);
|
enable_app_grouping(&matches, &config, &mut app);
|
||||||
enable_app_case_sensitive(&matches, &config, &mut app);
|
enable_app_case_sensitive(&matches, &config, &mut app);
|
||||||
enable_app_match_whole_word(&matches, &config, &mut app);
|
enable_app_match_whole_word(&matches, &config, &mut app);
|
||||||
enable_app_use_regex(&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
|
// Set up up tui and crossterm
|
||||||
let mut stdout_val = stdout();
|
let mut stdout_val = stdout();
|
||||||
|
@ -187,13 +197,12 @@ fn main() -> error::Result<()> {
|
||||||
if handle_key_event_or_break(event, &mut app, &rtx) {
|
if handle_key_event_or_break(event, &mut app, &rtx) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
handle_force_redraws(&mut app);
|
||||||
if app.update_process_gui {
|
}
|
||||||
update_final_process_list(&mut app);
|
BottomEvent::MouseInput(event) => {
|
||||||
app.update_process_gui = false;
|
handle_mouse_event(event, &mut app);
|
||||||
}
|
handle_force_redraws(&mut app);
|
||||||
}
|
}
|
||||||
BottomEvent::MouseInput(event) => handle_mouse_event(event, &mut app),
|
|
||||||
BottomEvent::Update(data) => {
|
BottomEvent::Update(data) => {
|
||||||
app.data_collection.eat_data(&data);
|
app.data_collection.eat_data(&data);
|
||||||
|
|
||||||
|
@ -201,7 +210,11 @@ fn main() -> error::Result<()> {
|
||||||
// Convert all data into tui-compliant components
|
// Convert all data into tui-compliant components
|
||||||
|
|
||||||
// Network
|
// Network
|
||||||
let network_data = convert_network_data_points(&app.data_collection);
|
let network_data = convert_network_data_points(
|
||||||
|
&app.data_collection,
|
||||||
|
app.net_state.display_time,
|
||||||
|
false,
|
||||||
|
);
|
||||||
app.canvas_data.network_data_rx = network_data.rx;
|
app.canvas_data.network_data_rx = network_data.rx;
|
||||||
app.canvas_data.network_data_tx = network_data.tx;
|
app.canvas_data.network_data_tx = network_data.tx;
|
||||||
app.canvas_data.rx_display = network_data.rx_display;
|
app.canvas_data.rx_display = network_data.rx_display;
|
||||||
|
@ -215,8 +228,16 @@ fn main() -> error::Result<()> {
|
||||||
// Temperatures
|
// Temperatures
|
||||||
app.canvas_data.temp_sensor_data = convert_temp_row(&app);
|
app.canvas_data.temp_sensor_data = convert_temp_row(&app);
|
||||||
// Memory
|
// Memory
|
||||||
app.canvas_data.mem_data = convert_mem_data_points(&app.data_collection);
|
app.canvas_data.mem_data = convert_mem_data_points(
|
||||||
app.canvas_data.swap_data = convert_swap_data_points(&app.data_collection);
|
&app.data_collection,
|
||||||
|
app.mem_state.display_time,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
app.canvas_data.swap_data = convert_swap_data_points(
|
||||||
|
&app.data_collection,
|
||||||
|
app.mem_state.display_time,
|
||||||
|
false,
|
||||||
|
);
|
||||||
let memory_and_swap_labels = convert_mem_labels(&app.data_collection);
|
let memory_and_swap_labels = convert_mem_labels(&app.data_collection);
|
||||||
app.canvas_data.mem_label = memory_and_swap_labels.0;
|
app.canvas_data.mem_label = memory_and_swap_labels.0;
|
||||||
app.canvas_data.swap_label = memory_and_swap_labels.1;
|
app.canvas_data.swap_label = memory_and_swap_labels.1;
|
||||||
|
@ -225,6 +246,8 @@ fn main() -> error::Result<()> {
|
||||||
app.canvas_data.cpu_data = convert_cpu_data_points(
|
app.canvas_data.cpu_data = convert_cpu_data_points(
|
||||||
app.app_config_fields.show_average_cpu,
|
app.app_config_fields.show_average_cpu,
|
||||||
&app.data_collection,
|
&app.data_collection,
|
||||||
|
app.cpu_state.display_time,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Pre-fill CPU if needed
|
// Pre-fill CPU if needed
|
||||||
|
@ -268,8 +291,8 @@ fn main() -> error::Result<()> {
|
||||||
|
|
||||||
fn handle_mouse_event(event: MouseEvent, app: &mut App) {
|
fn handle_mouse_event(event: MouseEvent, app: &mut App) {
|
||||||
match event {
|
match event {
|
||||||
MouseEvent::ScrollUp(_x, _y, _modifiers) => app.decrement_position_count(),
|
MouseEvent::ScrollUp(_x, _y, _modifiers) => app.handle_scroll_up(),
|
||||||
MouseEvent::ScrollDown(_x, _y, _modifiers) => app.increment_position_count(),
|
MouseEvent::ScrollDown(_x, _y, _modifiers) => app.handle_scroll_down(),
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -356,9 +379,9 @@ fn handle_key_event_or_break(
|
||||||
app.reset();
|
app.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Char('u') => app.clear_search(),
|
|
||||||
KeyCode::Char('a') => app.skip_cursor_beginning(),
|
KeyCode::Char('a') => app.skip_cursor_beginning(),
|
||||||
KeyCode::Char('e') => app.skip_cursor_end(),
|
KeyCode::Char('e') => app.skip_cursor_end(),
|
||||||
|
KeyCode::Char('u') => app.clear_search(),
|
||||||
// KeyCode::Char('j') => {}, // Move down
|
// KeyCode::Char('j') => {}, // Move down
|
||||||
// KeyCode::Char('k') => {}, // Move up
|
// KeyCode::Char('k') => {}, // Move up
|
||||||
// KeyCode::Char('h') => {}, // Move right
|
// KeyCode::Char('h') => {}, // Move right
|
||||||
|
@ -560,6 +583,48 @@ fn panic_hook(panic_info: &PanicInfo<'_>) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_force_redraws(app: &mut App) {
|
||||||
|
if app.force_update_processes {
|
||||||
|
update_final_process_list(app);
|
||||||
|
app.force_update_processes = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if app.cpu_state.force_update {
|
||||||
|
app.canvas_data.cpu_data = convert_cpu_data_points(
|
||||||
|
app.app_config_fields.show_average_cpu,
|
||||||
|
&app.data_collection,
|
||||||
|
app.cpu_state.display_time,
|
||||||
|
app.is_frozen,
|
||||||
|
);
|
||||||
|
app.cpu_state.force_update = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if app.mem_state.force_update {
|
||||||
|
app.canvas_data.mem_data = convert_mem_data_points(
|
||||||
|
&app.data_collection,
|
||||||
|
app.mem_state.display_time,
|
||||||
|
app.is_frozen,
|
||||||
|
);
|
||||||
|
app.canvas_data.swap_data = convert_swap_data_points(
|
||||||
|
&app.data_collection,
|
||||||
|
app.mem_state.display_time,
|
||||||
|
app.is_frozen,
|
||||||
|
);
|
||||||
|
app.mem_state.force_update = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if app.net_state.force_update {
|
||||||
|
let (rx, tx) = get_rx_tx_data_points(
|
||||||
|
&app.data_collection,
|
||||||
|
app.net_state.display_time,
|
||||||
|
app.is_frozen,
|
||||||
|
);
|
||||||
|
app.canvas_data.network_data_rx = rx;
|
||||||
|
app.canvas_data.network_data_tx = tx;
|
||||||
|
app.net_state.force_update = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn update_final_process_list(app: &mut App) {
|
fn update_final_process_list(app: &mut App) {
|
||||||
let mut filtered_process_data: Vec<ConvertedProcessData> = if app.is_grouped() {
|
let mut filtered_process_data: Vec<ConvertedProcessData> = if app.is_grouped() {
|
||||||
app.canvas_data
|
app.canvas_data
|
||||||
|
|
106
src/options.rs
106
src/options.rs
|
@ -27,6 +27,10 @@ pub struct ConfigFlags {
|
||||||
pub default_widget: Option<String>,
|
pub default_widget: Option<String>,
|
||||||
pub show_disabled_data: Option<bool>,
|
pub show_disabled_data: Option<bool>,
|
||||||
pub basic: Option<bool>,
|
pub basic: Option<bool>,
|
||||||
|
pub default_time_value: Option<u64>,
|
||||||
|
pub time_delta: Option<u64>,
|
||||||
|
pub autohide_time: Option<bool>,
|
||||||
|
pub hide_time: Option<bool>,
|
||||||
//disabled_cpu_cores: Option<Vec<u64>>, // TODO: [FEATURE] Enable disabling cores in config/flags
|
//disabled_cpu_cores: Option<Vec<u64>>, // TODO: [FEATURE] Enable disabling cores in config/flags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,30 +56,30 @@ pub struct ConfigColours {
|
||||||
|
|
||||||
pub fn get_update_rate_in_milliseconds(
|
pub fn get_update_rate_in_milliseconds(
|
||||||
update_rate: &Option<&str>, config: &Config,
|
update_rate: &Option<&str>, config: &Config,
|
||||||
) -> error::Result<u128> {
|
) -> 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) = update_rate {
|
||||||
update_rate.parse::<u128>()?
|
update_rate.parse::<u128>()?
|
||||||
} else if let Some(flags) = &config.flags {
|
} else if let Some(flags) = &config.flags {
|
||||||
if let Some(rate) = flags.rate {
|
if let Some(rate) = flags.rate {
|
||||||
rate as u128
|
rate as u128
|
||||||
} else {
|
} else {
|
||||||
DEFAULT_REFRESH_RATE_IN_MILLISECONDS
|
DEFAULT_REFRESH_RATE_IN_MILLISECONDS as u128
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DEFAULT_REFRESH_RATE_IN_MILLISECONDS
|
DEFAULT_REFRESH_RATE_IN_MILLISECONDS as u128
|
||||||
};
|
};
|
||||||
|
|
||||||
if update_rate_in_milliseconds < 250 {
|
if update_rate_in_milliseconds < 250 {
|
||||||
return Err(BottomError::InvalidArg(
|
return Err(BottomError::InvalidArg(
|
||||||
"Please set your update rate to be greater than 250 milliseconds.".to_string(),
|
"Please set your update rate to be at least 250 milliseconds.".to_string(),
|
||||||
));
|
));
|
||||||
} else if update_rate_in_milliseconds > u128::from(std::u64::MAX) {
|
} else if update_rate_in_milliseconds as u128 > std::u64::MAX as u128 {
|
||||||
return Err(BottomError::InvalidArg(
|
return Err(BottomError::InvalidArg(
|
||||||
"Please set your update rate to be less than unsigned INT_MAX.".to_string(),
|
"Please set your update rate to be at most unsigned INT_MAX.".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(update_rate_in_milliseconds)
|
Ok(update_rate_in_milliseconds as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_temperature_option(
|
pub fn get_temperature_option(
|
||||||
|
@ -178,6 +182,62 @@ pub fn get_use_basic_mode_option(matches: &clap::ArgMatches<'static>, config: &C
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_default_time_value_option(
|
||||||
|
matches: &clap::ArgMatches<'static>, config: &Config,
|
||||||
|
) -> error::Result<u64> {
|
||||||
|
let default_time = if let Some(default_time_value) = matches.value_of("DEFAULT_TIME_VALUE") {
|
||||||
|
default_time_value.parse::<u128>()?
|
||||||
|
} else if let Some(flags) = &config.flags {
|
||||||
|
if let Some(default_time_value) = flags.default_time_value {
|
||||||
|
default_time_value as u128
|
||||||
|
} else {
|
||||||
|
DEFAULT_TIME_MILLISECONDS as u128
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DEFAULT_TIME_MILLISECONDS as u128
|
||||||
|
};
|
||||||
|
|
||||||
|
if default_time < 30000 {
|
||||||
|
return Err(BottomError::InvalidArg(
|
||||||
|
"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),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(default_time as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_time_interval_option(
|
||||||
|
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 {
|
||||||
|
if let Some(time_interval) = flags.time_delta {
|
||||||
|
time_interval as u128
|
||||||
|
} else {
|
||||||
|
TIME_CHANGE_MILLISECONDS as u128
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TIME_CHANGE_MILLISECONDS as u128
|
||||||
|
};
|
||||||
|
|
||||||
|
if time_interval < 1000 {
|
||||||
|
return Err(BottomError::InvalidArg(
|
||||||
|
"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),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(time_interval as u64)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn enable_app_grouping(matches: &clap::ArgMatches<'static>, config: &Config, app: &mut App) {
|
pub fn enable_app_grouping(matches: &clap::ArgMatches<'static>, config: &Config, app: &mut App) {
|
||||||
if matches.is_present("GROUP_PROCESSES") {
|
if matches.is_present("GROUP_PROCESSES") {
|
||||||
app.toggle_grouping();
|
app.toggle_grouping();
|
||||||
|
@ -230,6 +290,38 @@ 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) {
|
||||||
|
if matches.is_present("HIDE_TIME") {
|
||||||
|
app.app_config_fields.hide_time = 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enable_autohide_time(matches: &clap::ArgMatches<'static>, config: &Config, app: &mut App) {
|
||||||
|
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;
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_default_widget(matches: &clap::ArgMatches<'static>, config: &Config) -> WidgetPosition {
|
pub fn get_default_widget(matches: &clap::ArgMatches<'static>, config: &Config) -> WidgetPosition {
|
||||||
if matches.is_present("CPU_WIDGET") {
|
if matches.is_present("CPU_WIDGET") {
|
||||||
return WidgetPosition::Cpu;
|
return WidgetPosition::Cpu;
|
||||||
|
|
|
@ -28,7 +28,61 @@ fn test_small_rate() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
.arg("249")
|
.arg("249")
|
||||||
.assert()
|
.assert()
|
||||||
.failure()
|
.failure()
|
||||||
.stderr(predicate::str::contains("rate to be greater than 250"));
|
.stderr(predicate::str::contains(
|
||||||
|
"Please set your update rate to be at least 250 milliseconds.",
|
||||||
|
));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_large_default_time() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
Command::new(get_os_binary_loc())
|
||||||
|
.arg("-t")
|
||||||
|
.arg("18446744073709551616")
|
||||||
|
.assert()
|
||||||
|
.failure()
|
||||||
|
.stderr(predicate::str::contains(
|
||||||
|
"Please set your default value to be at most",
|
||||||
|
));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_small_default_time() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
Command::new(get_os_binary_loc())
|
||||||
|
.arg("-t")
|
||||||
|
.arg("900")
|
||||||
|
.assert()
|
||||||
|
.failure()
|
||||||
|
.stderr(predicate::str::contains(
|
||||||
|
"Please set your default value to be at least",
|
||||||
|
));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_large_delta_time() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
Command::new(get_os_binary_loc())
|
||||||
|
.arg("-d")
|
||||||
|
.arg("18446744073709551616")
|
||||||
|
.assert()
|
||||||
|
.failure()
|
||||||
|
.stderr(predicate::str::contains(
|
||||||
|
"Please set your time delta to be at most",
|
||||||
|
));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_small_delta_time() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
Command::new(get_os_binary_loc())
|
||||||
|
.arg("-d")
|
||||||
|
.arg("900")
|
||||||
|
.assert()
|
||||||
|
.failure()
|
||||||
|
.stderr(predicate::str::contains(
|
||||||
|
"Please set your time delta to be at least",
|
||||||
|
));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +94,7 @@ fn test_large_rate() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
.assert()
|
.assert()
|
||||||
.failure()
|
.failure()
|
||||||
.stderr(predicate::str::contains(
|
.stderr(predicate::str::contains(
|
||||||
"rate to be less than unsigned INT_MAX.",
|
"Please set your update rate to be at most unsigned INT_MAX.",
|
||||||
));
|
));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
Loading…
Reference in New Issue