summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClement Tsang <34804052+ClementTsang@users.noreply.github.com>2020-09-02 22:02:49 -0400
committerGitHub <noreply@github.com>2020-09-02 22:02:49 -0400
commitcef3166cf87d0a769fb886049b9bf1e5faf75e32 (patch)
treef54fab112ed2ed18d39af1baaa15ede155de0c5a
parenta94907372839c7fd79bbd0e37d9cab8ccfa40790 (diff)
feature: Add ability to filter out disks and temp (#220)
You can now filter out disks and temp sensors by name via config.
-rw-r--r--.vscode/settings.json3
-rw-r--r--CHANGELOG.md4
-rw-r--r--CONTRIBUTING.md5
-rw-r--r--README.md57
-rw-r--r--assets/disk_filter_post.pngbin0 -> 17307 bytes
-rw-r--r--assets/disk_filter_post2.pngbin0 -> 17363 bytes
-rw-r--r--assets/disk_filter_pre.pngbin0 -> 27108 bytes
-rw-r--r--assets/summary_and_search.gifbin1790504 -> 912833 bytes
-rw-r--r--assets/temp_filter_post.pngbin0 -> 8165 bytes
-rw-r--r--assets/temp_filter_post2.pngbin0 -> 20943 bytes
-rw-r--r--assets/temp_filter_pre.pngbin0 -> 46535 bytes
-rw-r--r--src/app.rs26
-rw-r--r--src/app/data_harvester/disks.rs4
-rw-r--r--src/bin/main.rs8
-rw-r--r--src/canvas.rs4
-rw-r--r--src/canvas/widgets/network_graph.rs1
-rw-r--r--src/constants.rs1
-rw-r--r--src/data_conversion.rs83
-rw-r--r--src/lib.rs11
-rw-r--r--src/options.rs62
20 files changed, 217 insertions, 52 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index e03fdc59..531704fb 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -40,6 +40,7 @@
"crossterm",
"curr",
"czvf",
+ "denylist",
"fpath",
"fract",
"gnueabihf",
@@ -57,6 +58,7 @@
"noheader",
"ntdef",
"nuget",
+ "nvme",
"paren",
"pmem",
"prepush",
@@ -82,6 +84,7 @@
"virt",
"vsize",
"whitespaces",
+ "wifi",
"winapi",
"winget",
"winnt",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3f064ad8..5762280b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#208](https://github.com/ClementTsang/bottom/pull/208): Mouse support for tables and moving to widgets.
+- [#217](https://github.com/ClementTsang/bottom/pull/217): Unofficial ARM support.
+
+- [#220](https://github.com/ClementTsang/bottom/pull/220): Add ability to hide specific temperature and disk entries via config.
+
### Changes
- [#213](https://github.com/ClementTsang/bottom/pull/213), [#214](https://github.com/ClementTsang/bottom/pull/214): Updated help descriptions, added auto-complete generation.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index bb948b3e..2206f95d 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -46,10 +46,9 @@ If you want to help contribute by submitting a PR, by all means, I'm open! In re
- You can check clippy using `cargo clippy`.
- - I use [cargo-husky](https://github.com/rhysd/cargo-husky) to automatically run a clippy check on push. You can disable this in the `Cargo.toml` file if you find this annoying.
+ - I use [cargo-husky](https://github.com/rhysd/cargo-husky) to automatically run a `cargo clippy` and `cargo test` check.
-- You may notice that I have fern and log as dependencies; this is mostly for easy debugging via the `debug!()` macro. It writes to the
- `debug.log` file that will automatically be created if you run in debug mode (so `cargo run`).
+- You may notice that I have fern and log as dependencies; this is mostly for easy debugging via the `debug!()` macro. It writes to the `debug.log` file that will automatically be created if you run in debug mode (so `cargo run`).
And in regards to the pull request process:
diff --git a/README.md b/README.md
index a8a39388..0a19dde7 100644
--- a/README.md
+++ b/README.md
@@ -48,6 +48,7 @@ A cross-platform graphical process/system monitor with a customizable interface
- [Config flags](#config-flags)
- [Theming](#theming)
- [Layout](#layout)
+ - [Disk and temperature filtering](#disk-and-temperature-filtering)
- [Battery](#battery)
- [Compatibility](#compatibility)
- [Contribution](#contribution)
@@ -352,16 +353,16 @@ Note that the `and` operator takes precedence over the `or` operator.
#### General
-| | |
-| ------------ | --------------------------------------------------------------------------------------------------------------------- |
-| Mouse scroll | Table: Scroll<br>Chart: Zooms in or out by scrolling up or down respectively |
-| Mouse click | Selects the clicked widget. For tables, clicking can also select a specific entry. Can be disabled via options/flags. |
+| | |
+| ------ | ---------------------------------------------------------------------------------------------------------------- |
+| Scroll | Table: Scroll<br>Chart: Zooms in or out by scrolling up or down respectively |
+| Click | Selects the clicked widget. For tables, clicking can also select an entry.<br>Can be disabled via options/flags. |
#### CPU bindings
-| | |
-| ------------ | --------------------------------------------------------------------- |
-| Mouse scroll | Scrolling over an CPU core/average shows only that entry on the chart |
+| | |
+| ------ | --------------------------------------------------------------------- |
+| Scroll | Scrolling over an CPU core/average shows only that entry on the chart |
## Features
@@ -596,6 +597,48 @@ Furthermore, you can have duplicate widgets. This means you could do something l
and get the following CPU donut:
![CPU donut](./assets/cpu_layout.png)
+#### Disk and temperature filtering
+
+You can hide specific disks and temperature sensors by name in the config file via `disk_filter` and `temp_filter` respectively. Regex (`regex = true`) and case-sensitivity (`case_sensitive = true`) are supported, but are off by default.
+
+For example, let's say , given this disk list:
+
+![Disk filter not ignoring list](./assets/disk_filter_pre.png)
+
+I wish to _only_ show disks that follow the form `/dev/sda\d+`, or `/dev/nvme0n1p2`:
+
+```toml
+[disk_filter]
+is_list_ignored = false
+list = ["/dev/sda\\d+", "/dev/nvme0n1p2"]
+regex = true
+```
+
+![Disk filter not ignoring list](./assets/disk_filter_post.png)
+
+This would ignore anything that does not match either of these two conditions. If I instead wish to ignore anything that matches this list, then I can set `is_list_ignored = true` instead:
+
+![Disk filter ignoring list](./assets/disk_filter_post2.png)
+
+Likewise, I can do something similar for `temp_filter`:
+
+![Temp filter before](./assets/temp_filter_pre.png)
+
+If I, say, only wanted to see any entry with the words "cpu" or "wifi" in it, case sensitive:
+
+```toml
+[temp_filter]
+is_list_ignored = false
+list = ["cpu", "wifi"]
+case_sensitive = true
+```
+
+![Temp filter after](./assets/temp_filter_post.png)
+
+Now, flipping to `case_sensitive = false` would instead show:
+
+![Temp filter after with case sensitivity off](./assets/temp_filter_post2.png)
+
### Battery
You can get battery statistics (charge, time to fill/discharge, consumption in watts, and battery health) via the battery widget.
diff --git a/assets/disk_filter_post.png b/assets/disk_filter_post.png
new file mode 100644
index 00000000..741976d8
--- /dev/null
+++ b/assets/disk_filter_post.png
Binary files differ
diff --git a/assets/disk_filter_post2.png b/assets/disk_filter_post2.png
new file mode 100644
index 00000000..09c06de2
--- /dev/null
+++ b/assets/disk_filter_post2.png
Binary files differ
diff --git a/assets/disk_filter_pre.png b/assets/disk_filter_pre.png
new file mode 100644
index 00000000..3b8463b0
--- /dev/null
+++ b/assets/disk_filter_pre.png
Binary files differ
diff --git a/assets/summary_and_search.gif b/assets/summary_and_search.gif
index 730d07e1..d50a0355 100644
--- a/assets/summary_and_search.gif
+++ b/assets/summary_and_search.gif
Binary files differ
diff --git a/assets/temp_filter_post.png b/assets/temp_filter_post.png
new file mode 100644
index 00000000..23ac208e
--- /dev/null
+++ b/assets/temp_filter_post.png
Binary files differ
diff --git a/assets/temp_filter_post2.png b/assets/temp_filter_post2.png
new file mode 100644
index 00000000..88f40e64
--- /dev/null
+++ b/assets/temp_filter_post2.png
Binary files differ
diff --git a/assets/temp_filter_pre.png b/assets/temp_filter_pre.png
new file mode 100644
index 00000000..41a6282b
--- /dev/null
+++ b/assets/temp_filter_pre.png
Binary files differ
diff --git a/src/app.rs b/src/app.rs
index 8893a1c7..9ceb9d56 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -43,6 +43,18 @@ pub struct AppConfigFields {
pub disable_click: bool,
}
+/// For filtering out information
+pub struct DataFilters {
+ pub disk_filter: Option<Filter>,
+ pub temp_filter: Option<Filter>,
+}
+
+#[derive(Debug)]
+pub struct Filter {
+ pub is_list_ignored: bool,
+ pub list: Vec<regex::Regex>,
+}
+
#[derive(TypedBuilder)]
pub struct App {
#[builder(default = false, setter(skip))]
@@ -99,6 +111,7 @@ pub struct App {
pub widget_map: HashMap<u64, BottomWidget>,
pub current_widget: BottomWidget,
pub used_widgets: UsedWidgets,
+ pub filters: DataFilters,
}
impl App {
@@ -312,10 +325,9 @@ impl App {
pub fn toggle_sort(&mut self) {
match &self.current_widget.widget_type {
- // FIXME: [REFACTOR] Remove these @'s if unneeded, they were an idea but they're ultimately useless for me here...?
- widget_type @ BottomWidgetType::Proc | widget_type @ BottomWidgetType::ProcSort => {
+ BottomWidgetType::Proc | BottomWidgetType::ProcSort => {
let widget_id = self.current_widget.widget_id
- - match &widget_type {
+ - match &self.current_widget.widget_type {
BottomWidgetType::Proc => 0,
BottomWidgetType::ProcSort => 2,
_ => 0,
@@ -348,9 +360,9 @@ impl App {
pub fn invert_sort(&mut self) {
match &self.current_widget.widget_type {
- widget_type @ BottomWidgetType::Proc | widget_type @ BottomWidgetType::ProcSort => {
+ BottomWidgetType::Proc | BottomWidgetType::ProcSort => {
let widget_id = self.current_widget.widget_id
- - match &widget_type {
+ - match &self.current_widget.widget_type {
BottomWidgetType::Proc => 0,
BottomWidgetType::ProcSort => 2,
_ => 0,
@@ -1571,9 +1583,9 @@ impl App {
}
}
WidgetDirection::Down => match &self.current_widget.widget_type {
- proc_type @ BottomWidgetType::Proc | proc_type @ BottomWidgetType::ProcSort => {
+ BottomWidgetType::Proc | BottomWidgetType::ProcSort => {
let widget_id = self.current_widget.widget_id
- - match proc_type {
+ - match &self.current_widget.widget_type {
BottomWidgetType::ProcSort => 2,
_ => 0,
};
diff --git a/src/app/data_harvester/disks.rs b/src/app/data_harvester/disks.rs
index 948279e5..788f05ce 100644
--- a/src/app/data_harvester/disks.rs
+++ b/src/app/data_harvester/disks.rs
@@ -46,7 +46,9 @@ pub async fn get_sysinfo_disk_usage_list(
name: disk.get_name().to_string_lossy().into(),
mount_point: disk.get_mount_point().to_string_lossy().into(),
free_space: disk.get_available_space(),
- used_space: disk.get_total_space() - disk.get_available_space(),
+ used_space: disk
+ .get_total_space()
+ .saturating_sub(disk.get_available_space()),
total_space: disk.get_total_space(),
})
.collect::<Vec<DiskHarvest>>();
diff --git a/src/bin/main.rs b/src/bin/main.rs
index 7f8b9900..dd1cbabe 100644
--- a/src/bin/main.rs
+++ b/src/bin/main.rs
@@ -83,10 +83,7 @@ fn main() -> Result<()> {
create_event_thread(
sender,
reset_receiver,
- app.app_config_fields.use_current_cpu_total,
- app.app_config_fields.update_rate_in_milliseconds,
- app.app_config_fields.temperature_type.clone(),
- app.app_config_fields.show_average_cpu,
+ &app.app_config_fields,
app.used_widgets.clone(),
);
@@ -151,7 +148,8 @@ fn main() -> Result<()> {
// Disk
if app.used_widgets.use_disk {
- app.canvas_data.disk_data = convert_disk_row(&app.data_collection);
+ app.canvas_data.disk_data =
+ convert_disk_row(&app.data_collection, &app.filters.disk_filter);
}
// Temperatures
diff --git a/src/canvas.rs b/src/canvas.rs
index 7036bd49..61f74e4f 100644
--- a/src/canvas.rs
+++ b/src/canvas.rs
@@ -379,9 +379,9 @@ impl Painter {
app_state.current_widget.widget_id,
false,
),
- proc_type @ Proc | proc_type @ ProcSearch | proc_type @ ProcSort => {
+ Proc | ProcSearch | ProcSort => {
let widget_id = app_state.current_widget.widget_id
- - match proc_type {
+ - match &app_state.current_widget.widget_type {
ProcSearch => 1,
ProcSort => 2,
_ => 0,
diff --git a/src/canvas/widgets/network_graph.rs b/src/canvas/widgets/network_graph.rs
index 8a75ebd6..32887c32 100644
--- a/src/canvas/widgets/network_graph.rs
+++ b/src/canvas/widgets/network_graph.rs
@@ -67,7 +67,6 @@ impl NetworkGraphWidget for Painter {
// Update draw loc in widget map
// Note that in both cases, we always go to the same widget id so it's fine to do it like
// this lol.
- debug!("!@#!@");
if let Some(network_widget) = app_state.widget_map.get_mut(&widget_id) {
network_widget.top_left_corner = Some((draw_loc.x, draw_loc.y));
network_widget.bottom_right_corner =
diff --git a/src/constants.rs b/src/constants.rs
index d1772413..5d58848c 100644
--- a/src/constants.rs
+++ b/src/constants.rs
@@ -248,6 +248,7 @@ pub const DEFAULT_BATTERY_LAYOUT: &str = r##"
pub const DEFAULT_CONFIG_FILE_PATH: &str = "bottom/bottom.toml";
// Default config file
+// FIXME: Update the default config
pub const DEFAULT_CONFIG_CONTENT: &str = r##"
# This is a default config file for bottom. All of the settings are commented
# out by default; if you wish to change them uncomment and modify as you see
diff --git a/src/data_conversion.rs b/src/data_conversion.rs
index eaf655d0..bf38483e 100644
--- a/src/data_conversion.rs
+++ b/src/data_conversion.rs
@@ -4,7 +4,7 @@
use std::collections::HashMap;
use crate::{
- app::{data_farmer, data_harvester, App},
+ app::{data_farmer, data_harvester, App, Filter},
utils::gen_util::*,
};
@@ -83,40 +83,77 @@ pub struct ConvertedCpuData {
}
pub fn convert_temp_row(app: &App) -> Vec<Vec<String>> {
- let mut sensor_vector: Vec<Vec<String>> = Vec::new();
-
let current_data = &app.data_collection;
let temp_type = &app.app_config_fields.temperature_type;
+ let temp_filter = &app.filters.temp_filter;
- if current_data.temp_harvest.is_empty() {
- sensor_vector.push(vec!["No Sensors Found".to_string(), "".to_string()])
- } else {
- for sensor in &current_data.temp_harvest {
- sensor_vector.push(vec![
- match (&sensor.component_name, &sensor.component_label) {
- (Some(name), Some(label)) => format!("{}: {}", name, label),
- (None, Some(label)) => label.to_string(),
- (Some(name), None) => name.to_string(),
- (None, None) => String::default(),
- },
- (sensor.temperature.ceil() as u64).to_string()
- + match temp_type {
- data_harvester::temperature::TemperatureType::Celsius => "C",
- data_harvester::temperature::TemperatureType::Kelvin => "K",
- data_harvester::temperature::TemperatureType::Fahrenheit => "F",
- },
- ]);
- }
+ let mut sensor_vector: Vec<Vec<String>> = current_data
+ .temp_harvest
+ .iter()
+ .filter_map(|temp_harvest| {
+ let name = match (&temp_harvest.component_name, &temp_harvest.component_label) {
+ (Some(name), Some(label)) => format!("{}: {}", name, label),
+ (None, Some(label)) => label.to_string(),
+ (Some(name), None) => name.to_string(),
+ (None, None) => String::default(),
+ };
+
+ let to_keep = if let Some(temp_filter) = temp_filter {
+ let mut ret = temp_filter.is_list_ignored;
+ for r in &temp_filter.list {
+ if r.is_match(&name) {
+ ret = !temp_filter.is_list_ignored;
+ break;
+ }
+ }
+ ret
+ } else {
+ true
+ };
+
+ if to_keep {
+ Some(vec![
+ name,
+ (temp_harvest.temperature.ceil() as u64).to_string()
+ + match temp_type {
+ data_harvester::temperature::TemperatureType::Celsius => "C",
+ data_harvester::temperature::TemperatureType::Kelvin => "K",
+ data_harvester::temperature::TemperatureType::Fahrenheit => "F",
+ },
+ ])
+ } else {
+ None
+ }
+ })
+ .collect();
+
+ if sensor_vector.is_empty() {
+ sensor_vector.push(vec!["No Sensors Found".to_string(), "".to_string()]);
}
sensor_vector
}
-pub fn convert_disk_row(current_data: &data_farmer::DataCollection) -> Vec<Vec<String>> {
+pub fn convert_disk_row(
+ current_data: &data_farmer::DataCollection, disk_filter: &Option<Filter>,
+) -> Vec<Vec<String>> {
let mut disk_vector: Vec<Vec<String>> = Vec::new();
+
current_data
.disk_harvest
.iter()
+ .filter(|disk_harvest| {
+ if let Some(disk_filter) = disk_filter {
+ for r in &disk_filter.list {
+ if r.is_match(&disk_harvest.name) {
+ return !disk_filter.is_list_ignored;
+ }
+ }
+ disk_filter.is_list_ignored
+ } else {
+ true
+ }
+ })
.zip(&current_data.io_labels)
.for_each(|(disk, (io_read, io_write))| {
let converted_free_space = get_simple_byte_values(disk.free_space, false);
diff --git a/src/lib.rs b/src/lib.rs
index 207c2963..1b1c3e7b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -687,16 +687,21 @@ pub fn create_event_thread(
sender: std::sync::mpsc::Sender<
BottomEvent<crossterm::event::KeyEvent, crossterm::event::MouseEvent>,
>,
- reset_receiver: std::sync::mpsc::Receiver<ResetEvent>, use_current_cpu_total: bool,
- update_rate_in_milliseconds: u64, temp_type: data_harvester::temperature::TemperatureType,
- show_average_cpu: bool, used_widget_set: UsedWidgets,
+ reset_receiver: std::sync::mpsc::Receiver<ResetEvent>,
+ app_config_fields: &app::AppConfigFields, used_widget_set: UsedWidgets,
) {
+ let temp_type = app_config_fields.temperature_type.clone();
+ let use_current_cpu_total = app_config_fields.use_current_cpu_total;
+ let show_average_cpu = app_config_fields.show_average_cpu;
+ let update_rate_in_milliseconds = app_config_fields.update_rate_in_milliseconds;
+
thread::spawn(move || {
let mut data_state = data_harvester::DataCollector::default();
data_state.set_collected_data(used_widget_set);
data_state.set_temperature_type(temp_type);
data_state.set_use_current_cpu_total(use_current_cpu_total);
data_state.set_show_average_cpu(show_average_cpu);
+
data_state.init();
loop {
if let Ok(message) = reset_receiver.try_recv() {
diff --git a/src/options.rs b/src/options.rs
index e1a6e226..a8536d8a 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -1,3 +1,4 @@
+use regex::Regex;
use serde::Deserialize;
use std::collections::{HashMap, HashSet};
use std::time::Instant;
@@ -19,6 +20,8 @@ pub struct Config {
pub flags: Option<ConfigFlags>,
pub colors: Option<ConfigColours>,
pub row: Option<Vec<Row>>,
+ pub disk_filter: Option<IgnoreList>,
+ pub temp_filter: Option<IgnoreList>,
}
#[derive(Default, Deserialize)]
@@ -69,6 +72,14 @@ pub struct ConfigColours {
pub battery_colors: Option<Vec<String>>,
}
+#[derive(Default, Deserialize)]
+pub struct IgnoreList {
+ pub is_list_ignored: bool,
+ pub list: Vec<String>,
+ pub regex: Option<bool>,
+ pub case_sensitive: Option<bool>,
+}
+
pub fn build_app(
matches: &clap::ArgMatches<'static>, config: &Config, widget_layout: &BottomLayout,
default_widget_id: u64, default_widget_type_option: &Option<BottomWidgetType>,
@@ -249,6 +260,11 @@ pub fn build_app(
use_battery: used_widget_set.get(&Battery).is_some(),
};
+ let disk_filter =
+ get_ignore_list(&config.disk_filter).context("Update 'disk_filter' in your config file")?;
+ let temp_filter =
+ get_ignore_list(&config.temp_filter).context("Update 'temp_filter' in your config file")?;
+
Ok(App::builder()
.app_config_fields(app_config_fields)
.cpu_state(CpuState::init(cpu_state_map))
@@ -262,6 +278,10 @@ pub fn build_app(
.current_widget(widget_map.get(&initial_widget_id).unwrap().clone()) // I think the unwrap is fine here
.widget_map(widget_map)
.used_widgets(used_widgets)
+ .filters(DataFilters {
+ disk_filter,
+ temp_filter,
+ })
.build())
}
@@ -665,3 +685,45 @@ pub fn get_use_battery(matches: &clap::ArgMatches<'static>, config: &Config) ->
}
false
}
+
+pub fn get_ignore_list(ignore_list: &Option<IgnoreList>) -> error::Result<Option<Filter>> {
+ if let Some(ignore_list) = ignore_list {
+ let list: Result<Vec<_>, _> = ignore_list
+ .list
+ .iter()
+ .map(|name| {
+ let use_regex = if let Some(use_regex) = ignore_list.regex {
+ use_regex
+ } else {
+ false
+ };
+ let use_cs = if let Some(use_cs) = ignore_list.case_sensitive {
+ use_cs
+ } else {
+ false
+ };
+
+ let escaped_string: String;
+ let res = format!(
+ "{}{}",
+ if use_cs { "" } else { "(?i)" },
+ if use_regex {
+ name
+ } else {
+ escaped_string = regex::escape(name);
+ &escaped_string
+ }
+ );
+
+ Regex::new(&res)
+ })
+ .collect();
+
+ Ok(Some(Filter {
+ list: list?,
+ is_list_ignored: ignore_list.is_list_ignored,
+ }))
+ } else {
+ Ok(None)
+ }
+}