refactor: various bug fixes and code removal

This commit is contained in:
ClementTsang 2021-09-27 16:28:48 -04:00
parent 9089231bc4
commit f02daa0a2b
29 changed files with 154 additions and 661 deletions

12
Cargo.lock generated
View File

@ -266,7 +266,6 @@ dependencies = [
"thiserror", "thiserror",
"toml", "toml",
"tui", "tui",
"typed-builder",
"unicode-segmentation", "unicode-segmentation",
"unicode-width", "unicode-width",
"winapi", "winapi",
@ -1529,17 +1528,6 @@ dependencies = [
"unicode-width", "unicode-width",
] ]
[[package]]
name = "typed-builder"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a46ee5bd706ff79131be9c94e7edcb82b703c487766a114434e5790361cf08c5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.14.0" version = "1.14.0"

View File

@ -59,7 +59,6 @@ thiserror = "1.0.24"
textwrap = "0.14.2" textwrap = "0.14.2"
toml = "0.5.8" toml = "0.5.8"
tui = { version = "0.16.0", features = ["crossterm"], default-features = false } tui = { version = "0.16.0", features = ["crossterm"], default-features = false }
typed-builder = "0.9.0"
unicode-segmentation = "1.8.0" unicode-segmentation = "1.8.0"
unicode-width = "0.1" unicode-width = "0.1"

View File

@ -19,9 +19,7 @@ pub use filter::*;
use layout_manager::*; use layout_manager::*;
pub use widgets::*; pub use widgets::*;
use crate::{ use crate::{constants, units::data_units::DataUnit, utils::error::Result, BottomEvent, Pid};
canvas, constants, units::data_units::DataUnit, utils::error::Result, BottomEvent, Pid,
};
use self::event::{ComponentEventResult, EventResult, ReturnSignal}; use self::event::{ComponentEventResult, EventResult, ReturnSignal};
@ -87,7 +85,7 @@ pub struct AppConfigFields {
pub hide_time: bool, pub hide_time: bool,
pub autohide_time: bool, pub autohide_time: bool,
pub use_old_network_legend: bool, pub use_old_network_legend: bool,
pub table_gap: u16, // TODO: [Config, Refactor] Just make this a bool... pub table_gap: bool,
pub disable_click: bool, pub disable_click: bool,
pub no_write: bool, pub no_write: bool,
pub show_table_scroll_position: bool, pub show_table_scroll_position: bool,
@ -101,7 +99,7 @@ pub struct AppConfigFields {
/// the data collected at that instant. /// the data collected at that instant.
pub enum FrozenState { pub enum FrozenState {
NotFrozen, NotFrozen,
Frozen(DataCollection), Frozen(Box<DataCollection>),
} }
impl Default for FrozenState { impl Default for FrozenState {
@ -115,8 +113,6 @@ pub struct AppState {
to_delete_process_list: Option<(String, Vec<Pid>)>, to_delete_process_list: Option<(String, Vec<Pid>)>,
pub canvas_data: canvas::DisplayableData,
pub data_collection: DataCollection, pub data_collection: DataCollection,
pub is_expanded: bool, pub is_expanded: bool,
@ -167,7 +163,6 @@ impl AppState {
// Use defaults. // Use defaults.
dd_err: Default::default(), dd_err: Default::default(),
to_delete_process_list: Default::default(), to_delete_process_list: Default::default(),
canvas_data: Default::default(),
data_collection: Default::default(), data_collection: Default::default(),
is_expanded: Default::default(), is_expanded: Default::default(),
delete_dialog_state: Default::default(), delete_dialog_state: Default::default(),
@ -186,7 +181,7 @@ impl AppState {
if matches!(self.frozen_state, FrozenState::Frozen(_)) { if matches!(self.frozen_state, FrozenState::Frozen(_)) {
self.frozen_state = FrozenState::NotFrozen; self.frozen_state = FrozenState::NotFrozen;
} else { } else {
self.frozen_state = FrozenState::Frozen(self.data_collection.clone()); self.frozen_state = FrozenState::Frozen(Box::new(self.data_collection.clone()));
} }
} }
@ -418,7 +413,6 @@ impl AppState {
if was_id_already_selected { if was_id_already_selected {
returned_result = self.convert_widget_event_result(result); returned_result = self.convert_widget_event_result(result);
break;
} else { } else {
// If the weren't equal, *force* a redraw, and correct the layout tree. // If the weren't equal, *force* a redraw, and correct the layout tree.
correct_layout_last_selections( correct_layout_last_selections(
@ -427,8 +421,8 @@ impl AppState {
); );
let _ = self.convert_widget_event_result(result); let _ = self.convert_widget_event_result(result);
returned_result = EventResult::Redraw; returned_result = EventResult::Redraw;
break;
} }
break;
} }
SelectableType::Unselectable => { SelectableType::Unselectable => {
let result = widget.handle_mouse_event(event); let result = widget.handle_mouse_event(event);

View File

@ -26,17 +26,14 @@ use crate::{
}; };
use regex::Regex; use regex::Regex;
pub type TimeOffset = f64;
pub type Value = f64;
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct TimedData { pub struct TimedData {
pub rx_data: Value, pub rx_data: f64,
pub tx_data: Value, pub tx_data: f64,
pub cpu_data: Vec<Value>, pub cpu_data: Vec<f64>,
pub load_avg_data: [f32; 3], pub load_avg_data: [f32; 3],
pub mem_data: Option<Value>, pub mem_data: Option<f64>,
pub swap_data: Option<Value>, pub swap_data: Option<f64>,
} }
/// AppCollection represents the pooled data stored within the main app /// AppCollection represents the pooled data stored within the main app

View File

@ -78,7 +78,8 @@ impl Default for ProcessSorting {
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct ProcessHarvest { pub struct ProcessHarvest {
pub pid: Pid, pub pid: Pid,
pub parent_pid: Option<Pid>, // Remember, parent_pid 0 is root... pub parent_pid: Option<Pid>,
pub children_pids: Vec<Pid>,
pub cpu_usage_percent: f64, pub cpu_usage_percent: f64,
pub mem_usage_percent: f64, pub mem_usage_percent: f64,
pub mem_usage_bytes: u64, pub mem_usage_bytes: u64,
@ -93,10 +94,11 @@ pub struct ProcessHarvest {
pub process_state: String, pub process_state: String,
pub process_state_char: char, pub process_state_char: char,
/// This is the *effective* user ID. /// This is the *effective* user ID. This is only used on Unix platforms.
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
pub uid: libc::uid_t, pub uid: libc::uid_t,
/// This is the process' user. This is only used on Unix platforms.
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
pub user: Cow<'static, str>, pub user: Cow<'static, str>,
} }

View File

@ -203,6 +203,7 @@ fn read_proc(
ProcessHarvest { ProcessHarvest {
pid: process.pid, pid: process.pid,
parent_pid, parent_pid,
children_pids: vec![],
cpu_usage_percent, cpu_usage_percent,
mem_usage_percent, mem_usage_percent,
mem_usage_bytes, mem_usage_bytes,

View File

@ -89,6 +89,7 @@ pub fn get_process_data(
process_vector.push(ProcessHarvest { process_vector.push(ProcessHarvest {
pid: process_val.pid(), pid: process_val.pid(),
parent_pid: process_val.parent(), parent_pid: process_val.parent(),
children_pids: vec![],
name, name,
command, command,
mem_usage_percent: if mem_total_kb > 0 { mem_usage_percent: if mem_total_kb > 0 {

View File

@ -58,6 +58,7 @@ pub fn get_process_data(
process_vector.push(ProcessHarvest { process_vector.push(ProcessHarvest {
pid: process_val.pid(), pid: process_val.pid(),
parent_pid: process_val.parent(), parent_pid: process_val.parent(),
children_pids: vec![],
name, name,
command, command,
mem_usage_percent: if mem_total_kb > 0 { mem_usage_percent: if mem_total_kb > 0 {

View File

@ -13,7 +13,6 @@ use fxhash::FxHashMap;
use indextree::{Arena, NodeId}; use indextree::{Arena, NodeId};
use std::cmp::min; use std::cmp::min;
use tui::layout::Rect; use tui::layout::Rect;
use typed_builder::*;
use crate::app::widgets::Widget; use crate::app::widgets::Widget;
@ -21,130 +20,6 @@ use super::{
event::SelectionAction, AppConfigFields, CpuGraph, TimeGraph, TmpBottomWidget, UsedWidgets, event::SelectionAction, AppConfigFields, CpuGraph, TimeGraph, TmpBottomWidget, UsedWidgets,
}; };
/// Represents a more usable representation of the layout, derived from the
/// config.
#[derive(Clone, Debug)]
pub struct BottomLayout {
pub rows: Vec<OldBottomRow>,
pub total_row_height_ratio: u32,
}
/// Represents a single row in the layout.
#[derive(Clone, Debug, TypedBuilder)]
pub struct OldBottomRow {
pub children: Vec<OldBottomCol>,
#[builder(default = 1)]
pub total_col_ratio: u32,
#[builder(default = 1)]
pub row_height_ratio: u32,
#[builder(default = false)]
pub canvas_handle_height: bool,
#[builder(default = false)]
pub flex_grow: bool,
}
/// Represents a single column in the layout. We assume that even if the column
/// contains only ONE element, it is still a column (rather than either a col or
/// a widget, as per the config, for simplicity's sake).
#[derive(Clone, Debug, TypedBuilder)]
pub struct OldBottomCol {
pub children: Vec<BottomColRow>,
#[builder(default = 1)]
pub total_col_row_ratio: u32,
#[builder(default = 1)]
pub col_width_ratio: u32,
#[builder(default = false)]
pub canvas_handle_width: bool,
#[builder(default = false)]
pub flex_grow: bool,
}
#[derive(Clone, Default, Debug, TypedBuilder)]
pub struct BottomColRow {
pub children: Vec<BottomWidget>,
#[builder(default = 1)]
pub total_widget_ratio: u32,
#[builder(default = 1)]
pub col_row_height_ratio: u32,
#[builder(default = false)]
pub canvas_handle_height: bool,
#[builder(default = false)]
pub flex_grow: bool,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum WidgetDirection {
Left,
Right,
Up,
Down,
}
impl WidgetDirection {
pub fn is_opposite(&self, other_direction: &WidgetDirection) -> bool {
match &self {
WidgetDirection::Left => *other_direction == WidgetDirection::Right,
WidgetDirection::Right => *other_direction == WidgetDirection::Left,
WidgetDirection::Up => *other_direction == WidgetDirection::Down,
WidgetDirection::Down => *other_direction == WidgetDirection::Up,
}
}
}
/// Represents a single widget.
#[derive(Debug, Default, Clone, TypedBuilder)]
pub struct BottomWidget {
pub widget_type: BottomWidgetType,
pub widget_id: u64,
#[builder(default = 1)]
pub width_ratio: u32,
#[builder(default = None)]
pub left_neighbour: Option<u64>,
#[builder(default = None)]
pub right_neighbour: Option<u64>,
#[builder(default = None)]
pub up_neighbour: Option<u64>,
#[builder(default = None)]
pub down_neighbour: Option<u64>,
/// If set to true, the canvas will override any ratios.
#[builder(default = false)]
pub canvas_handle_width: bool,
/// Whether we want this widget to take up all available room (and ignore any ratios).
#[builder(default = false)]
pub flex_grow: bool,
/// The value is the direction to bounce, as well as the parent offset.
#[builder(default = None)]
pub parent_reflector: Option<(WidgetDirection, u64)>,
/// Top left corner when drawn, for mouse click detection. (x, y)
#[builder(default = None)]
pub top_left_corner: Option<(u16, u16)>,
/// Bottom right corner when drawn, for mouse click detection. (x, y)
#[builder(default = None)]
pub bottom_right_corner: Option<(u16, u16)>,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum BottomWidgetType { pub enum BottomWidgetType {
Empty, Empty,
@ -238,8 +113,6 @@ Supported widget names:
} }
} }
// --- New stuff ---
/// Represents a row in the layout tree. /// Represents a row in the layout tree.
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
pub struct RowLayout { pub struct RowLayout {
@ -380,7 +253,7 @@ pub fn create_layout_tree(
BottomWidgetType::Proc => { BottomWidgetType::Proc => {
widget_lookup_map.insert( widget_lookup_map.insert(
widget_id, widget_id,
ProcessManager::new(process_defaults) ProcessManager::new(process_defaults, app_config_fields)
.width(width) .width(width)
.height(height) .height(height)
.basic_mode(app_config_fields.use_basic_mode) .basic_mode(app_config_fields.use_basic_mode)
@ -391,7 +264,7 @@ pub fn create_layout_tree(
BottomWidgetType::Temp => { BottomWidgetType::Temp => {
widget_lookup_map.insert( widget_lookup_map.insert(
widget_id, widget_id,
TempTable::default() TempTable::from_config(app_config_fields)
.set_temp_type(app_config_fields.temperature_type.clone()) .set_temp_type(app_config_fields.temperature_type.clone())
.width(width) .width(width)
.height(height) .height(height)
@ -403,7 +276,7 @@ pub fn create_layout_tree(
BottomWidgetType::Disk => { BottomWidgetType::Disk => {
widget_lookup_map.insert( widget_lookup_map.insert(
widget_id, widget_id,
DiskTable::default() DiskTable::from_config(app_config_fields)
.width(width) .width(width)
.height(height) .height(height)
.basic_mode(app_config_fields.use_basic_mode) .basic_mode(app_config_fields.use_basic_mode)
@ -620,7 +493,7 @@ pub fn create_layout_tree(
/// ///
/// We can do this by just going through the ancestors, starting from the widget itself. /// We can do this by just going through the ancestors, starting from the widget itself.
pub fn correct_layout_last_selections(arena: &mut Arena<LayoutNode>, selected: NodeId) { pub fn correct_layout_last_selections(arena: &mut Arena<LayoutNode>, selected: NodeId) {
let mut selected_ancestors = selected.ancestors(&arena).collect::<Vec<_>>(); let mut selected_ancestors = selected.ancestors(arena).collect::<Vec<_>>();
let prev_node = selected_ancestors.pop(); let prev_node = selected_ancestors.pop();
if let Some(mut prev_node) = prev_node { if let Some(mut prev_node) = prev_node {
for node in selected_ancestors { for node in selected_ancestors {

View File

@ -1,78 +1,7 @@
// Copied from SO: https://stackoverflow.com/a/55231715 //! This file is meant to house (OS specific) implementations on how to kill processes.
#[cfg(target_os = "windows")]
use winapi::{ #[cfg(target_os = "windows")]
shared::{minwindef::DWORD, ntdef::HANDLE}, pub(crate) mod windows;
um::{
processthreadsapi::{OpenProcess, TerminateProcess},
winnt::{PROCESS_QUERY_INFORMATION, PROCESS_TERMINATE},
},
};
/// This file is meant to house (OS specific) implementations on how to kill processes.
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
use crate::utils::error::BottomError; pub(crate) mod unix;
use crate::Pid;
#[cfg(target_os = "windows")]
struct Process(HANDLE);
#[cfg(target_os = "windows")]
impl Process {
fn open(pid: DWORD) -> Result<Process, String> {
let pc = unsafe { OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_TERMINATE, 0, pid) };
if pc.is_null() {
return Err("OpenProcess".to_string());
}
Ok(Process(pc))
}
fn kill(self) -> Result<(), String> {
let result = unsafe { TerminateProcess(self.0, 1) };
if result == 0 {
return Err("Failed to kill process".to_string());
}
Ok(())
}
}
/// Kills a process, given a PID, for unix.
#[cfg(target_family = "unix")]
pub fn kill_process_given_pid(pid: Pid, signal: usize) -> crate::utils::error::Result<()> {
let output = unsafe { libc::kill(pid as i32, signal as i32) };
if output != 0 {
// We had an error...
let err_code = std::io::Error::last_os_error().raw_os_error();
let err = match err_code {
Some(libc::ESRCH) => "the target process did not exist.",
Some(libc::EPERM) => "the calling process does not have the permissions to terminate the target process(es).",
Some(libc::EINVAL) => "an invalid signal was specified.",
_ => "Unknown error occurred."
};
return if let Some(err_code) = err_code {
Err(BottomError::GenericError(format!(
"Error code {} - {}",
err_code, err,
)))
} else {
Err(BottomError::GenericError(format!(
"Error code ??? - {}",
err,
)))
};
}
Ok(())
}
/// Kills a process, given a PID, for windows.
#[cfg(target_os = "windows")]
pub fn kill_process_given_pid(pid: Pid) -> crate::utils::error::Result<()> {
{
let process = Process::open(pid as DWORD)?;
process.kill()?;
}
Ok(())
}

View File

@ -0,0 +1,31 @@
use crate::utils::error::BottomError;
use crate::Pid;
/// Kills a process, given a PID, for unix.
pub(crate) fn kill_process_given_pid(pid: Pid, signal: usize) -> crate::utils::error::Result<()> {
let output = unsafe { libc::kill(pid as i32, signal as i32) };
if output != 0 {
// We had an error...
let err_code = std::io::Error::last_os_error().raw_os_error();
let err = match err_code {
Some(libc::ESRCH) => "the target process did not exist.",
Some(libc::EPERM) => "the calling process does not have the permissions to terminate the target process(es).",
Some(libc::EINVAL) => "an invalid signal was specified.",
_ => "Unknown error occurred."
};
return if let Some(err_code) = err_code {
Err(BottomError::GenericError(format!(
"Error code {} - {}",
err_code, err,
)))
} else {
Err(BottomError::GenericError(format!(
"Error code ??? - {}",
err,
)))
};
}
Ok(())
}

View File

@ -0,0 +1,40 @@
// Copied from SO: https://stackoverflow.com/a/55231715
use winapi::{
shared::{minwindef::DWORD, ntdef::HANDLE},
um::{
processthreadsapi::{OpenProcess, TerminateProcess},
winnt::{PROCESS_QUERY_INFORMATION, PROCESS_TERMINATE},
},
};
pub(crate) struct Process(HANDLE);
impl Process {
pub(crate) fn open(pid: DWORD) -> Result<Process, String> {
let pc = unsafe { OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_TERMINATE, 0, pid) };
if pc.is_null() {
return Err("OpenProcess".to_string());
}
Ok(Process(pc))
}
pub(crate) fn kill(self) -> Result<(), String> {
let result = unsafe { TerminateProcess(self.0, 1) };
if result == 0 {
return Err("Failed to kill process".to_string());
}
Ok(())
}
}
/// Kills a process, given a PID, for windows.
#[cfg(target_os = "windows")]
pub fn kill_process_given_pid(pid: Pid) -> crate::utils::error::Result<()> {
{
let process = Process::open(pid as DWORD)?;
process.kill()?;
}
Ok(())
}

View File

@ -138,11 +138,6 @@ pub trait Widget {
/// Returns the desired height from the [`Widget`]. /// Returns the desired height from the [`Widget`].
fn height(&self) -> LayoutRule; fn height(&self) -> LayoutRule;
/// Returns whether this [`Widget`] can be expanded. The default implementation returns `true`.
fn expandable(&self) -> bool {
true
}
/// Returns whether this [`Widget`] can be selected. The default implementation returns [`SelectableType::Selectable`]. /// Returns whether this [`Widget`] can be selected. The default implementation returns [`SelectableType::Selectable`].
fn selectable_type(&self) -> SelectableType { fn selectable_type(&self) -> SelectableType {
SelectableType::Selectable SelectableType::Selectable

View File

@ -33,6 +33,11 @@ impl SortMenu {
} }
} }
pub fn try_show_gap(mut self, show_gap: bool) -> Self {
self.table = self.table.try_show_gap(show_gap);
self
}
/// Updates the index of the [`SortMenu`]. /// Updates the index of the [`SortMenu`].
pub fn set_index(&mut self, index: usize) { pub fn set_index(&mut self, index: usize) {
self.table.scrollable.set_index(index); self.table.scrollable.set_index(index);

View File

@ -274,8 +274,8 @@ where
st st
} }
pub fn default_ltr(mut self, ltr: bool) -> Self { pub fn try_show_gap(mut self, show_gap: bool) -> Self {
self.table = self.table.default_ltr(ltr); self.table = self.table.try_show_gap(show_gap);
self self
} }

View File

@ -399,7 +399,10 @@ where
f.render_widget(block, block_area); f.render_widget(block, block_area);
return; return;
} }
let table_gap = if !self.show_gap || inner_area.height < TABLE_GAP_HEIGHT_LIMIT { let table_gap = if !self.show_gap
|| (data.len() + 2 > inner_area.height.into()
&& inner_area.height < TABLE_GAP_HEIGHT_LIMIT)
{
0 0
} else { } else {
1 1

View File

@ -185,6 +185,7 @@ impl Widget for BatteryTable {
split_area[0].width, split_area[0].width,
split_area[0].height, split_area[0].height,
); );
// FIXME: [URGENT] See if this should be changed; TABLE_GAP_HEIGHT_LIMIT should be removed maybe. May also need to grab the table gap from the config?
let data_area = let data_area =
if inner_area.height >= TABLE_GAP_HEIGHT_LIMIT && split_area[1].height > 0 { if inner_area.height >= TABLE_GAP_HEIGHT_LIMIT && split_area[1].height > 0 {
Rect::new( Rect::new(

View File

@ -56,7 +56,8 @@ impl CpuGraph {
SimpleColumn::new_flex("CPU".into(), 0.5), SimpleColumn::new_flex("CPU".into(), 0.5),
SimpleColumn::new_hard("Use".into(), None), SimpleColumn::new_hard("Use".into(), None),
]) ])
.default_ltr(false); .default_ltr(false)
.try_show_gap(app_config_fields.table_gap);
let legend_position = if app_config_fields.left_legend { let legend_position = if app_config_fields.left_legend {
CpuGraphLegendPosition::Left CpuGraphLegendPosition::Left
} else { } else {

View File

@ -4,8 +4,8 @@ use tui::{backend::Backend, layout::Rect, widgets::Borders, Frame};
use crate::{ use crate::{
app::{ app::{
data_farmer::DataCollection, event::ComponentEventResult, data_farmer::DataCollection, event::ComponentEventResult,
sort_text_table::SimpleSortableColumn, text_table::TextTableData, Component, TextTable, sort_text_table::SimpleSortableColumn, text_table::TextTableData, AppConfigFields,
Widget, Component, TextTable, Widget,
}, },
canvas::Painter, canvas::Painter,
data_conversion::convert_disk_row, data_conversion::convert_disk_row,
@ -25,8 +25,9 @@ pub struct DiskTable {
show_scroll_index: bool, show_scroll_index: bool,
} }
impl Default for DiskTable { impl DiskTable {
fn default() -> Self { /// Creates a [`DiskTable`] from a config.
pub fn from_config(app_config_fields: &AppConfigFields) -> Self {
let table = TextTable::new(vec![ let table = TextTable::new(vec![
SimpleSortableColumn::new_flex("Disk".into(), None, false, 0.2), SimpleSortableColumn::new_flex("Disk".into(), None, false, 0.2),
SimpleSortableColumn::new_flex("Mount".into(), None, false, 0.2), SimpleSortableColumn::new_flex("Mount".into(), None, false, 0.2),
@ -35,7 +36,8 @@ impl Default for DiskTable {
SimpleSortableColumn::new_hard("Total".into(), None, false, Some(6)), SimpleSortableColumn::new_hard("Total".into(), None, false, Some(6)),
SimpleSortableColumn::new_hard("R/s".into(), None, false, Some(7)), SimpleSortableColumn::new_hard("R/s".into(), None, false, Some(7)),
SimpleSortableColumn::new_hard("W/s".into(), None, false, Some(7)), SimpleSortableColumn::new_hard("W/s".into(), None, false, Some(7)),
]); ])
.try_show_gap(app_config_fields.table_gap);
Self { Self {
table, table,
@ -47,9 +49,7 @@ impl Default for DiskTable {
show_scroll_index: false, show_scroll_index: false,
} }
} }
}
impl DiskTable {
/// Sets the width. /// Sets the width.
pub fn width(mut self, width: LayoutRule) -> Self { pub fn width(mut self, width: LayoutRule) -> Self {
self.width = width; self.width = width;

View File

@ -599,6 +599,7 @@ impl OldNetGraph {
SimpleColumn::new_flex("Total RX".into(), 0.25), SimpleColumn::new_flex("Total RX".into(), 0.25),
SimpleColumn::new_flex("Total TX".into(), 0.25), SimpleColumn::new_flex("Total TX".into(), 0.25),
]) ])
.try_show_gap(config.table_gap)
.unselectable(), .unselectable(),
bounds: Rect::default(), bounds: Rect::default(),
width: LayoutRule::default(), width: LayoutRule::default(),
@ -646,11 +647,14 @@ impl Widget for OldNetGraph {
&mut self, painter: &Painter, f: &mut Frame<'_, B>, area: Rect, selected: bool, &mut self, painter: &Painter, f: &mut Frame<'_, B>, area: Rect, selected: bool,
expanded: bool, expanded: bool,
) { ) {
const CONSTRAINTS: [Constraint; 2] = [Constraint::Min(0), Constraint::Length(4)]; let constraints = [
Constraint::Min(0),
Constraint::Length(if self.table.show_gap { 5 } else { 4 }),
];
let split_area = Layout::default() let split_area = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints(CONSTRAINTS) .constraints(constraints)
.split(area); .split(area);
let graph_area = split_area[0]; let graph_area = split_area[0];

View File

@ -20,7 +20,7 @@ use crate::{
query::*, query::*,
text_table::DesiredColumnWidth, text_table::DesiredColumnWidth,
widgets::tui_stuff::BlockBuilder, widgets::tui_stuff::BlockBuilder,
DataCollection, AppConfigFields, DataCollection,
}, },
canvas::Painter, canvas::Painter,
data_conversion::get_string_with_bytes, data_conversion::get_string_with_bytes,
@ -273,7 +273,7 @@ pub struct ProcessManager {
impl ProcessManager { impl ProcessManager {
/// Creates a new [`ProcessManager`]. /// Creates a new [`ProcessManager`].
pub fn new(process_defaults: &ProcessDefaults) -> Self { pub fn new(process_defaults: &ProcessDefaults, config: &AppConfigFields) -> Self {
let process_table_columns = vec![ let process_table_columns = vec![
ProcessSortColumn::new(ProcessSortType::Pid), ProcessSortColumn::new(ProcessSortType::Pid),
ProcessSortColumn::new(ProcessSortType::Name), ProcessSortColumn::new(ProcessSortType::Name),
@ -290,8 +290,10 @@ impl ProcessManager {
let mut manager = Self { let mut manager = Self {
bounds: Rect::default(), bounds: Rect::default(),
sort_menu: SortMenu::new(process_table_columns.len()), sort_menu: SortMenu::new(process_table_columns.len()).try_show_gap(config.table_gap),
process_table: SortableTextTable::new(process_table_columns).default_sort_index(2), process_table: SortableTextTable::new(process_table_columns)
.default_sort_index(2)
.try_show_gap(config.table_gap),
search_input: TextInput::default(), search_input: TextInput::default(),
search_block_bounds: Rect::default(), search_block_bounds: Rect::default(),
dd_multi: MultiKey::register(vec!['d', 'd']), // TODO: [Optimization] Maybe use something static/const/arrayvec?... dd_multi: MultiKey::register(vec!['d', 'd']), // TODO: [Optimization] Maybe use something static/const/arrayvec?...
@ -494,6 +496,12 @@ impl ProcessManager {
self.search_modifiers.toggle_regex(); self.search_modifiers.toggle_regex();
ComponentEventResult::Signal(ReturnSignal::Update) ComponentEventResult::Signal(ReturnSignal::Update)
} }
/// Toggles tree mode.
fn toggle_tree_mode(&mut self) -> ComponentEventResult {
self.in_tree_mode = !self.in_tree_mode;
ComponentEventResult::Signal(ReturnSignal::Update)
}
} }
impl Component for ProcessManager { impl Component for ProcessManager {
@ -567,8 +575,7 @@ impl Component for ProcessManager {
// Collapse a branch // Collapse a branch
} }
KeyCode::Char('t') | KeyCode::F(5) => { KeyCode::Char('t') | KeyCode::F(5) => {
self.in_tree_mode = !self.in_tree_mode; return self.toggle_tree_mode();
return ComponentEventResult::Redraw;
} }
KeyCode::Char('s') | KeyCode::F(6) => { KeyCode::Char('s') | KeyCode::F(6) => {
return self.open_sort(); return self.open_sort();

View File

@ -5,7 +5,7 @@ use crate::{
app::{ app::{
data_farmer::DataCollection, data_harvester::temperature::TemperatureType, data_farmer::DataCollection, data_harvester::temperature::TemperatureType,
event::ComponentEventResult, sort_text_table::SimpleSortableColumn, event::ComponentEventResult, sort_text_table::SimpleSortableColumn,
text_table::TextTableData, Component, TextTable, Widget, text_table::TextTableData, AppConfigFields, Component, TextTable, Widget,
}, },
canvas::Painter, canvas::Painter,
data_conversion::convert_temp_row, data_conversion::convert_temp_row,
@ -24,13 +24,15 @@ pub struct TempTable {
show_scroll_index: bool, show_scroll_index: bool,
} }
impl Default for TempTable { impl TempTable {
fn default() -> Self { /// Creates a [`TempTable`] from a config.
pub fn from_config(app_config_fields: &AppConfigFields) -> Self {
let table = TextTable::new(vec![ let table = TextTable::new(vec![
SimpleSortableColumn::new_flex("Sensor".into(), None, false, 0.8), SimpleSortableColumn::new_flex("Sensor".into(), None, false, 0.8),
SimpleSortableColumn::new_hard("Temp".into(), None, false, Some(5)), SimpleSortableColumn::new_hard("Temp".into(), None, false, Some(5)),
]) ])
.default_ltr(false); .default_ltr(false)
.try_show_gap(app_config_fields.table_gap);
Self { Self {
table, table,
@ -43,9 +45,7 @@ impl Default for TempTable {
show_scroll_index: false, show_scroll_index: false,
} }
} }
}
impl TempTable {
/// Sets the [`TemperatureType`] for the [`TempTable`]. /// Sets the [`TemperatureType`] for the [`TempTable`].
pub fn set_temp_type(mut self, temp_type: TemperatureType) -> Self { pub fn set_temp_type(mut self, temp_type: TemperatureType) -> Self {
self.temp_type = temp_type; self.temp_type = temp_type;

View File

@ -1,4 +1,4 @@
use std::{collections::HashMap, str::FromStr}; use std::str::FromStr;
use fxhash::FxHashMap; use fxhash::FxHashMap;
use indextree::{Arena, NodeId}; use indextree::{Arena, NodeId};
@ -17,47 +17,18 @@ use crate::{
app::{ app::{
self, self,
layout_manager::{generate_layout, ColLayout, LayoutNode, RowLayout}, layout_manager::{generate_layout, ColLayout, LayoutNode, RowLayout},
text_table::TextTableData,
widgets::{Component, Widget}, widgets::{Component, Widget},
DialogState, TmpBottomWidget, DialogState, TmpBottomWidget,
}, },
constants::*, constants::*,
data_conversion::{ConvertedBatteryData, ConvertedCpuData, ConvertedProcessData},
options::Config, options::Config,
utils::error, utils::error,
utils::error::BottomError, utils::error::BottomError,
Pid,
}; };
mod canvas_colours; mod canvas_colours;
mod dialogs; mod dialogs;
/// Point is of time, data
type Point = (f64, f64);
#[derive(Default)]
pub struct DisplayableData {
pub rx_display: String,
pub tx_display: String,
pub total_rx_display: String,
pub total_tx_display: String,
pub network_data_rx: Vec<Point>,
pub network_data_tx: Vec<Point>,
pub disk_data: TextTableData,
pub temp_sensor_data: TextTableData,
pub single_process_data: HashMap<Pid, ConvertedProcessData>, // Contains single process data, key is PID
pub stringified_process_data_map: HashMap<NodeId, Vec<(Vec<(String, Option<String>)>, bool)>>, // Represents the row and whether it is disabled, key is the widget ID
pub mem_labels: Option<(String, String)>,
pub swap_labels: Option<(String, String)>,
pub mem_data: Vec<Point>,
pub swap_data: Vec<Point>,
pub load_avg_data: [f32; 3],
pub cpu_data: Vec<ConvertedCpuData>,
pub battery_data: Vec<ConvertedBatteryData>,
}
#[derive(Debug)] #[derive(Debug)]
pub enum ColourScheme { pub enum ColourScheme {
Default, Default,
@ -208,7 +179,7 @@ impl Painter {
}) })
.split(vertical_dialog_chunk[1]); .split(vertical_dialog_chunk[1]);
help_dialog.draw_help(&self, f, middle_dialog_chunk[1]); help_dialog.draw_help(self, f, middle_dialog_chunk[1]);
} else if app_state.delete_dialog_state.is_showing_dd { } else if app_state.delete_dialog_state.is_showing_dd {
// TODO: [Drawing] Better dd sizing needs the paragraph wrap feature from tui-rs to be pushed to // TODO: [Drawing] Better dd sizing needs the paragraph wrap feature from tui-rs to be pushed to
// complete... but for now it's pretty close! // complete... but for now it's pretty close!
@ -306,8 +277,7 @@ impl Painter {
fn traverse_and_draw_tree<B: Backend>( fn traverse_and_draw_tree<B: Backend>(
node: NodeId, arena: &Arena<LayoutNode>, f: &mut Frame<'_, B>, node: NodeId, arena: &Arena<LayoutNode>, f: &mut Frame<'_, B>,
lookup_map: &mut FxHashMap<NodeId, TmpBottomWidget>, painter: &Painter, lookup_map: &mut FxHashMap<NodeId, TmpBottomWidget>, painter: &Painter,
canvas_data: &DisplayableData, selected_id: NodeId, offset_x: u16, selected_id: NodeId, offset_x: u16, offset_y: u16,
offset_y: u16,
) { ) {
if let Some(layout_node) = arena.get(node).map(|n| n.get()) { if let Some(layout_node) = arena.get(node).map(|n| n.get()) {
match layout_node { match layout_node {
@ -320,7 +290,6 @@ impl Painter {
f, f,
lookup_map, lookup_map,
painter, painter,
canvas_data,
selected_id, selected_id,
offset_x + bound.x, offset_x + bound.x,
offset_y + bound.y, offset_y + bound.y,
@ -371,23 +340,12 @@ impl Painter {
let root = &app_state.layout_tree_root; let root = &app_state.layout_tree_root;
let arena = &mut app_state.layout_tree; let arena = &mut app_state.layout_tree;
let canvas_data = &app_state.canvas_data;
let selected_id = app_state.selected_widget; let selected_id = app_state.selected_widget;
generate_layout(*root, arena, draw_area, &app_state.widget_lookup_map); generate_layout(*root, arena, draw_area, &app_state.widget_lookup_map);
let lookup_map = &mut app_state.widget_lookup_map; let lookup_map = &mut app_state.widget_lookup_map;
traverse_and_draw_tree( traverse_and_draw_tree(*root, arena, f, lookup_map, self, selected_id, 0, 0);
*root,
arena,
f,
lookup_map,
self,
canvas_data,
selected_id,
0,
0,
);
} }
})?; })?;

View File

@ -10,10 +10,8 @@ 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 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 AUTOHIDE_TIMEOUT_MILLISECONDS: u64 = 5000; // 5 seconds to autohide
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: u64 = 1000; pub const DEFAULT_REFRESH_RATE_IN_MILLISECONDS: u64 = 1000;
pub const MAX_KEY_TIMEOUT_IN_MILLISECONDS: u64 = 1000;
// Limits for when we should stop showing table gaps/labels (anything less means not shown) // Limits for when we should stop showing table gaps/labels (anything less means not shown)
pub const TABLE_GAP_HEIGHT_LIMIT: u16 = 5; pub const TABLE_GAP_HEIGHT_LIMIT: u16 = 5;
@ -30,14 +28,6 @@ pub const MAX_SIGNAL: usize = 31;
// Side borders // Side borders
pub static SIDE_BORDERS: Lazy<tui::widgets::Borders> = pub static SIDE_BORDERS: Lazy<tui::widgets::Borders> =
Lazy::new(|| tui::widgets::Borders::from_bits_truncate(20)); Lazy::new(|| tui::widgets::Borders::from_bits_truncate(20));
pub static TOP_LEFT_RIGHT: Lazy<tui::widgets::Borders> =
Lazy::new(|| tui::widgets::Borders::from_bits_truncate(22));
pub static BOTTOM_LEFT_RIGHT: Lazy<tui::widgets::Borders> =
Lazy::new(|| tui::widgets::Borders::from_bits_truncate(28));
pub static DEFAULT_TEXT_STYLE: Lazy<tui::style::Style> =
Lazy::new(|| tui::style::Style::default().fg(tui::style::Color::Gray));
pub static DEFAULT_HEADER_STYLE: Lazy<tui::style::Style> =
Lazy::new(|| tui::style::Style::default().fg(tui::style::Color::LightBlue));
// Colour profiles // Colour profiles
pub static DEFAULT_LIGHT_MODE_COLOUR_PALETTE: Lazy<ConfigColours> = Lazy::new(|| ConfigColours { pub static DEFAULT_LIGHT_MODE_COLOUR_PALETTE: Lazy<ConfigColours> = Lazy::new(|| ConfigColours {

View File

@ -546,16 +546,6 @@ pub fn convert_network_data_points(
} }
} }
pub enum ProcessGroupingType {
Grouped,
Ungrouped,
}
pub enum ProcessNamingType {
Name,
Path,
}
/// Given read/s, write/s, total read, and total write values, return 4 strings that represent read/s, write/s, total read, and total write /// Given read/s, write/s, total read, and total write values, return 4 strings that represent read/s, write/s, total read, and total write
pub fn get_disk_io_strings( pub fn get_disk_io_strings(
rps: u64, wps: u64, total_read: u64, total_write: u64, rps: u64, wps: u64, total_read: u64, total_write: u64,
@ -579,142 +569,15 @@ pub fn get_string_with_bytes(value: u64) -> String {
} }
} }
/// Because we needed to UPDATE data entries rather than REPLACING entries, we instead update fn tree_process_data(
/// the existing vector. filtered_process_data: &[ConvertedProcessData], is_using_command: bool,
pub fn convert_process_data( sorting_type: &ProcessSorting, is_sort_descending: bool,
current_data: &DataCollection, ) -> Vec<ConvertedProcessData> {
existing_converted_process_data: &mut HashMap<Pid, ConvertedProcessData>,
#[cfg(target_family = "unix")] user_table: &mut data_harvester::processes::UserTable,
) {
// TODO: [Feature] Thread highlighting and hiding support; can we also count number of threads per process and display it as a column?
// For macOS see https://github.com/hishamhm/htop/pull/848/files
let mut complete_pid_set: fxhash::FxHashSet<Pid> =
existing_converted_process_data.keys().copied().collect();
for process in &current_data.process_harvest {
let (read_per_sec, write_per_sec, total_read, total_write) = get_disk_io_strings(
process.read_bytes_per_sec,
process.write_bytes_per_sec,
process.total_read_bytes,
process.total_write_bytes,
);
let mem_usage_str = get_binary_bytes(process.mem_usage_bytes);
let user = {
#[cfg(target_family = "unix")]
{
user_table.get_uid_to_username_mapping(process.uid).ok()
}
#[cfg(not(target_family = "unix"))]
{
None
}
};
if let Some(process_entry) = existing_converted_process_data.get_mut(&process.pid) {
complete_pid_set.remove(&process.pid);
// Very dumb way to see if there's PID reuse...
if process_entry.ppid == process.parent_pid {
process_entry.name = process.name.to_string();
process_entry.command = process.command.to_string();
process_entry.cpu_percent_usage = process.cpu_usage_percent;
process_entry.mem_percent_usage = process.mem_usage_percent;
process_entry.mem_usage_bytes = process.mem_usage_bytes;
process_entry.mem_usage_str = mem_usage_str;
process_entry.group_pids = vec![process.pid];
process_entry.read_per_sec = read_per_sec;
process_entry.write_per_sec = write_per_sec;
process_entry.total_read = total_read;
process_entry.total_write = total_write;
process_entry.rps_f64 = process.read_bytes_per_sec as f64;
process_entry.wps_f64 = process.write_bytes_per_sec as f64;
process_entry.tr_f64 = process.total_read_bytes as f64;
process_entry.tw_f64 = process.total_write_bytes as f64;
process_entry.process_state = process.process_state.to_owned();
process_entry.process_char = process.process_state_char;
process_entry.process_description_prefix = None;
process_entry.is_disabled_entry = false;
process_entry.user = user;
} else {
// ...I hate that I can't combine if let and an if statement in one line...
*process_entry = ConvertedProcessData {
pid: process.pid,
ppid: process.parent_pid,
is_thread: None,
name: process.name.to_string(),
command: process.command.to_string(),
cpu_percent_usage: process.cpu_usage_percent,
mem_percent_usage: process.mem_usage_percent,
mem_usage_bytes: process.mem_usage_bytes,
mem_usage_str,
group_pids: vec![process.pid],
read_per_sec,
write_per_sec,
total_read,
total_write,
rps_f64: process.read_bytes_per_sec as f64,
wps_f64: process.write_bytes_per_sec as f64,
tr_f64: process.total_read_bytes as f64,
tw_f64: process.total_write_bytes as f64,
process_state: process.process_state.to_owned(),
process_char: process.process_state_char,
process_description_prefix: None,
is_disabled_entry: false,
is_collapsed_entry: false,
user,
};
}
} else {
existing_converted_process_data.insert(
process.pid,
ConvertedProcessData {
pid: process.pid,
ppid: process.parent_pid,
is_thread: None,
name: process.name.to_string(),
command: process.command.to_string(),
cpu_percent_usage: process.cpu_usage_percent,
mem_percent_usage: process.mem_usage_percent,
mem_usage_bytes: process.mem_usage_bytes,
mem_usage_str,
group_pids: vec![process.pid],
read_per_sec,
write_per_sec,
total_read,
total_write,
rps_f64: process.read_bytes_per_sec as f64,
wps_f64: process.write_bytes_per_sec as f64,
tr_f64: process.total_read_bytes as f64,
tw_f64: process.total_write_bytes as f64,
process_state: process.process_state.to_owned(),
process_char: process.process_state_char,
process_description_prefix: None,
is_disabled_entry: false,
is_collapsed_entry: false,
user,
},
);
}
}
// Now clean up any spare entries that weren't visited, to avoid clutter:
complete_pid_set.iter().for_each(|pid| {
existing_converted_process_data.remove(pid);
})
}
const BRANCH_ENDING: char = '└'; const BRANCH_ENDING: char = '└';
const BRANCH_VERTICAL: char = '│'; const BRANCH_VERTICAL: char = '│';
const BRANCH_SPLIT: char = '├'; const BRANCH_SPLIT: char = '├';
const BRANCH_HORIZONTAL: char = '─'; const BRANCH_HORIZONTAL: char = '─';
fn tree_process_data(
filtered_process_data: &[ConvertedProcessData], is_using_command: bool,
sorting_type: &ProcessSorting, is_sort_descending: bool,
) -> Vec<ConvertedProcessData> {
// TODO: [Feature] Option to sort usage by total branch usage or individual value usage? // TODO: [Feature] Option to sort usage by total branch usage or individual value usage?
// Let's first build up a (really terrible) parent -> child mapping... // Let's first build up a (really terrible) parent -> child mapping...
@ -1178,166 +1041,6 @@ fn tree_process_data(
.collect::<Vec<_>>() .collect::<Vec<_>>()
} }
// FIXME: [URGENT] Delete this
// // TODO: [Optimization] This is an easy target for optimization, too many to_strings!
// fn stringify_process_data(
// proc_widget_state: &ProcWidgetState, finalized_process_data: &[ConvertedProcessData],
// ) -> Vec<(Vec<(String, Option<String>)>, bool)> {
// let is_proc_widget_grouped = proc_widget_state.is_grouped;
// let is_using_command = proc_widget_state.is_using_command;
// let is_tree = proc_widget_state.is_tree_mode;
// let mem_enabled = proc_widget_state.columns.is_enabled(&ProcessSorting::Mem);
// finalized_process_data
// .iter()
// .map(|process| {
// (
// vec![
// (
// if is_proc_widget_grouped {
// process.group_pids.len().to_string()
// } else {
// process.pid.to_string()
// },
// None,
// ),
// (
// if is_tree {
// if let Some(prefix) = &process.process_description_prefix {
// prefix.clone()
// } else {
// String::default()
// }
// } else if is_using_command {
// process.command.clone()
// } else {
// process.name.clone()
// },
// None,
// ),
// (format!("{:.1}%", process.cpu_percent_usage), None),
// (
// if mem_enabled {
// if process.mem_usage_bytes <= GIBI_LIMIT {
// format!("{:.0}{}", process.mem_usage_str.0, process.mem_usage_str.1)
// } else {
// format!("{:.1}{}", process.mem_usage_str.0, process.mem_usage_str.1)
// }
// } else {
// format!("{:.1}%", process.mem_percent_usage)
// },
// None,
// ),
// (process.read_per_sec.clone(), None),
// (process.write_per_sec.clone(), None),
// (process.total_read.clone(), None),
// (process.total_write.clone(), None),
// #[cfg(target_family = "unix")]
// (
// if let Some(user) = &process.user {
// user.clone()
// } else {
// "N/A".to_string()
// },
// None,
// ),
// (
// process.process_state.clone(),
// Some(process.process_char.to_string()),
// ),
// ],
// process.is_disabled_entry,
// )
// })
// .collect()
// }
/// Takes a set of converted process data and groups it together.
///
/// To be honest, I really don't like how this is done, even though I've rewritten this like 3 times.
fn group_process_data(
single_process_data: &[ConvertedProcessData], is_using_command: bool,
) -> Vec<ConvertedProcessData> {
#[derive(Clone, Default, Debug)]
struct SingleProcessData {
pub pid: Pid,
pub cpu_percent_usage: f64,
pub mem_percent_usage: f64,
pub mem_usage_bytes: u64,
pub group_pids: Vec<Pid>,
pub read_per_sec: f64,
pub write_per_sec: f64,
pub total_read: f64,
pub total_write: f64,
pub process_state: String,
}
let mut grouped_hashmap: HashMap<String, SingleProcessData> = std::collections::HashMap::new();
single_process_data.iter().for_each(|process| {
let entry = grouped_hashmap
.entry(if is_using_command {
process.command.to_string()
} else {
process.name.to_string()
})
.or_insert(SingleProcessData {
pid: process.pid,
..SingleProcessData::default()
});
(*entry).cpu_percent_usage += process.cpu_percent_usage;
(*entry).mem_percent_usage += process.mem_percent_usage;
(*entry).mem_usage_bytes += process.mem_usage_bytes;
(*entry).group_pids.push(process.pid);
(*entry).read_per_sec += process.rps_f64;
(*entry).write_per_sec += process.wps_f64;
(*entry).total_read += process.tr_f64;
(*entry).total_write += process.tw_f64;
});
grouped_hashmap
.iter()
.map(|(identifier, process_details)| {
let p = process_details.clone();
let (read_per_sec, write_per_sec, total_read, total_write) = get_disk_io_strings(
p.read_per_sec as u64,
p.write_per_sec as u64,
p.total_read as u64,
p.total_write as u64,
);
ConvertedProcessData {
pid: p.pid,
ppid: None,
is_thread: None,
name: identifier.to_string(),
command: identifier.to_string(),
cpu_percent_usage: p.cpu_percent_usage,
mem_percent_usage: p.mem_percent_usage,
mem_usage_bytes: p.mem_usage_bytes,
mem_usage_str: get_decimal_bytes(p.mem_usage_bytes),
group_pids: p.group_pids,
read_per_sec,
write_per_sec,
total_read,
total_write,
rps_f64: p.read_per_sec,
wps_f64: p.write_per_sec,
tr_f64: p.total_read,
tw_f64: p.total_write,
process_state: p.process_state,
process_description_prefix: None,
process_char: char::default(),
is_disabled_entry: false,
is_collapsed_entry: false,
user: None,
}
})
.collect::<Vec<_>>()
}
#[cfg(feature = "battery")] #[cfg(feature = "battery")]
pub fn convert_battery_harvest(current_data: &DataCollection) -> Vec<ConvertedBatteryData> { pub fn convert_battery_harvest(current_data: &DataCollection) -> Vec<ConvertedBatteryData> {
current_data current_data

View File

@ -42,7 +42,7 @@ pub mod clap;
pub mod constants; pub mod constants;
pub mod data_conversion; pub mod data_conversion;
pub mod options; pub mod options;
pub mod units; pub(crate) mod units;
#[cfg(target_family = "windows")] #[cfg(target_family = "windows")]
pub type Pid = usize; pub type Pid = usize;

View File

@ -211,11 +211,7 @@ pub fn build_app(matches: &clap::ArgMatches<'static>, config: &mut Config) -> Re
hide_time: get_hide_time(matches, config), hide_time: get_hide_time(matches, config),
autohide_time, autohide_time,
use_old_network_legend: get_use_old_network_legend(matches, config), use_old_network_legend: get_use_old_network_legend(matches, config),
table_gap: if get_hide_table_gap(matches, config) { table_gap: !get_hide_table_gap(matches, config),
0
} else {
1
},
disable_click: get_disable_click(matches, config), disable_click: get_disable_click(matches, config),
// no_write: get_no_write(matches, config), // no_write: get_no_write(matches, config),
no_write: false, no_write: false,
@ -551,18 +547,6 @@ fn get_use_battery(matches: &clap::ArgMatches<'static>, config: &Config) -> bool
false false
} }
#[allow(dead_code)]
fn get_no_write(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
if matches.is_present("no_write") {
return true;
} else if let Some(flags) = &config.flags {
if let Some(no_write) = flags.no_write {
return no_write;
}
}
false
}
fn get_ignore_list(ignore_list: &Option<IgnoreList>) -> error::Result<Option<Filter>> { fn get_ignore_list(ignore_list: &Option<IgnoreList>) -> error::Result<Option<Filter>> {
if let Some(ignore_list) = ignore_list { if let Some(ignore_list) = ignore_list {
let list: Result<Vec<_>, _> = ignore_list let list: Result<Vec<_>, _> = ignore_list

View File

@ -31,9 +31,6 @@ pub enum BottomError {
/// An error to represent errors with querying. /// An error to represent errors with querying.
#[error("Query error, {0}")] #[error("Query error, {0}")]
QueryError(Cow<'static, str>), QueryError(Cow<'static, str>),
/// An error that just signifies something minor went wrong; no message.
#[error("Minor error.")]
MinorError,
/// An error to represent errors with procfs /// An error to represent errors with procfs
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
#[error("Procfs error, {0}")] #[error("Procfs error, {0}")]

View File

@ -7,7 +7,6 @@ pub const TERA_LIMIT: u64 = 1_000_000_000_000;
pub const KIBI_LIMIT: u64 = 1024; pub const KIBI_LIMIT: u64 = 1024;
pub const MEBI_LIMIT: u64 = 1_048_576; pub const MEBI_LIMIT: u64 = 1_048_576;
pub const GIBI_LIMIT: u64 = 1_073_741_824; pub const GIBI_LIMIT: u64 = 1_073_741_824;
pub const TEBI_LIMIT: u64 = 1_099_511_627_776;
pub const KILO_LIMIT_F64: f64 = 1000.0; pub const KILO_LIMIT_F64: f64 = 1000.0;
pub const MEGA_LIMIT_F64: f64 = 1_000_000.0; pub const MEGA_LIMIT_F64: f64 = 1_000_000.0;
@ -30,16 +29,6 @@ pub const LOG_GIBI_LIMIT: f64 = 30.0;
pub const LOG_TEBI_LIMIT: f64 = 40.0; pub const LOG_TEBI_LIMIT: f64 = 40.0;
pub const LOG_PEBI_LIMIT: f64 = 50.0; pub const LOG_PEBI_LIMIT: f64 = 50.0;
pub const LOG_KILO_LIMIT_U32: u32 = 3;
pub const LOG_MEGA_LIMIT_U32: u32 = 6;
pub const LOG_GIGA_LIMIT_U32: u32 = 9;
pub const LOG_TERA_LIMIT_U32: u32 = 12;
pub const LOG_KIBI_LIMIT_U32: u32 = 10;
pub const LOG_MEBI_LIMIT_U32: u32 = 20;
pub const LOG_GIBI_LIMIT_U32: u32 = 30;
pub const LOG_TEBI_LIMIT_U32: u32 = 40;
/// Returns a tuple containing the value and the unit in bytes. In units of 1024. /// Returns a tuple containing the value and the unit in bytes. In units of 1024.
/// This only supports up to a tebi. Note the "single" unit will have a space appended to match the others if /// This only supports up to a tebi. Note the "single" unit will have a space appended to match the others if
/// `spacing` is true. /// `spacing` is true.