mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-27 15:44:17 +02:00
refactor: various bug fixes and code removal
This commit is contained in:
parent
9089231bc4
commit
f02daa0a2b
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -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"
|
||||||
|
@ -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"
|
||||||
|
|
||||||
|
16
src/app.rs
16
src/app.rs
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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>,
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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(())
|
|
||||||
}
|
|
||||||
|
31
src/app/process_killer/unix.rs
Normal file
31
src/app/process_killer/unix.rs
Normal 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(())
|
||||||
|
}
|
40
src/app/process_killer/windows.rs
Normal file
40
src/app/process_killer/windows.rs
Normal 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(())
|
||||||
|
}
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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(
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
|
@ -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];
|
||||||
|
@ -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();
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
|
||||||
/// the existing vector.
|
|
||||||
pub fn convert_process_data(
|
|
||||||
current_data: &DataCollection,
|
|
||||||
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 ¤t_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_VERTICAL: char = '│';
|
|
||||||
const BRANCH_SPLIT: char = '├';
|
|
||||||
const BRANCH_HORIZONTAL: char = '─';
|
|
||||||
|
|
||||||
fn tree_process_data(
|
fn tree_process_data(
|
||||||
filtered_process_data: &[ConvertedProcessData], is_using_command: bool,
|
filtered_process_data: &[ConvertedProcessData], is_using_command: bool,
|
||||||
sorting_type: &ProcessSorting, is_sort_descending: bool,
|
sorting_type: &ProcessSorting, is_sort_descending: bool,
|
||||||
) -> Vec<ConvertedProcessData> {
|
) -> Vec<ConvertedProcessData> {
|
||||||
|
const BRANCH_ENDING: char = '└';
|
||||||
|
const BRANCH_VERTICAL: char = '│';
|
||||||
|
const BRANCH_SPLIT: char = '├';
|
||||||
|
const BRANCH_HORIZONTAL: char = '─';
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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}")]
|
||||||
|
@ -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.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user