diff --git a/.vscode/settings.json b/.vscode/settings.json index 6002bb85..9a10ba3f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -65,6 +65,7 @@ "hjkl", "htop", "indexmap", + "iwlwifi", "keybinds", "le", "libc", @@ -112,6 +113,7 @@ "use", "use curr usage", "utime", + "virbr", "virt", "vsize", "whitespaces", diff --git a/CHANGELOG.md b/CHANGELOG.md index 5db3e343..de00824c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#379](https://github.com/ClementTsang/bottom/pull/379): Adds `--process_command` flag and corresponding config option to default to showing a process' command. +- [#381](https://github.com/ClementTsang/bottom/pull/381): Adds a filter in the config file for network interfaces. + ## Changes - [#372](https://github.com/ClementTsang/bottom/pull/372): Hides the SWAP graph and legend in normal mode if SWAP is 0. diff --git a/README.md b/README.md index 943a001e..8adbc580 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,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) + - [Disk and temperature filtering](#disk-temperature-and-network-filtering) - [Battery](#battery) - [Compatibility](#compatibility) - [FAQ](#faq) @@ -670,9 +670,9 @@ 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 +#### Disk, temperature, and network 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. +You can hide specific disks, temperature sensors, and networks by name in the config file via `disk_filter`, `temp_filter`, and `net_filter` respectively. Regex (`regex = true`), case-sensitivity (`case_sensitive = true`), and matching only the entire word (`whole_word = true`) are supported, but are off by default. For example, let's say , given this disk list: @@ -712,6 +712,20 @@ Now, flipping to `case_sensitive = false` would instead show: ![Temp filter after with case sensitivity off](./assets/temp_filter_post2.png) +Lastly, let's say I want to filter out _exactly_ "iwlwifi_1" from my results. I could do: + +```toml +[temp_filter] +is_list_ignored = true +list = ["iwlwifi_1"] +case_sensitive = true +whole_word = true +``` + +This will match the entire word, "iwlwifi_1", and ignore any result that exactly matches it: + +![Temp filter after with whole_word](./assets/temp_filter_post3.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/temp_filter_post3.png b/assets/temp_filter_post3.png new file mode 100644 index 00000000..f84886d1 Binary files /dev/null and b/assets/temp_filter_post3.png differ diff --git a/sample_configs/demo_config.toml b/sample_configs/demo_config.toml index f7fcb6b0..e6c5cf71 100644 --- a/sample_configs/demo_config.toml +++ b/sample_configs/demo_config.toml @@ -13,19 +13,3 @@ whole_word = false regex = true default_widget_type = "cpu" default_widget_count = 1 - -[colors] -# Based on gruvbox: https://github.com/morhetz/gruvbox -table_header_color="#458588" -widget_title_color="#cc241d" -cpu_core_colors=["#cc241d", "#98971a", "#d79921", "#458588", "#b16286", "#689d6a", "#fb4934", "#b8bb26", "#fabd2f", "#83a598"] -ram_color="#fb4934" -swap_color="#fabd2f" -rx_color="#458588" -tx_color="#689d6a" -border_color="#ebdbb2" -highlighted_border_color="#fe8019" -text_color="#ebdbb2" -graph_color="#ebdbb2" -selected_text_color="#282828" -selected_bg_color="#458588" diff --git a/src/app.rs b/src/app.rs index 7fae7cf0..5b2dbe90 100644 --- a/src/app.rs +++ b/src/app.rs @@ -57,12 +57,14 @@ pub struct AppConfigFields { } /// For filtering out information +#[derive(Debug, Clone)] pub struct DataFilters { pub disk_filter: Option, pub temp_filter: Option, + pub net_filter: Option, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Filter { pub is_list_ignored: bool, pub list: Vec, diff --git a/src/app/data_harvester.rs b/src/app/data_harvester.rs index 63d9a07c..0fc8c6a5 100644 --- a/src/app/data_harvester.rs +++ b/src/app/data_harvester.rs @@ -14,6 +14,8 @@ use crate::app::layout_manager::UsedWidgets; use futures::join; +use super::DataFilters; + pub mod batteries; pub mod cpu; pub mod disks; @@ -96,15 +98,15 @@ pub struct DataCollector { battery_list: Option>, #[cfg(target_os = "linux")] page_file_size_kb: u64, + filters: DataFilters, } -impl Default for DataCollector { - fn default() -> Self { - // trace!("Creating default data collector..."); +impl DataCollector { + pub fn new(filters: DataFilters) -> Self { DataCollector { data: Data::default(), #[cfg(not(target_os = "linux"))] - sys: System::new_with_specifics(sysinfo::RefreshKind::new()), // FIXME: Make this run on only macOS and Windows. + sys: System::new_with_specifics(sysinfo::RefreshKind::new()), #[cfg(target_os = "linux")] previous_cpu_times: vec![], #[cfg(target_os = "linux")] @@ -132,11 +134,10 @@ impl Default for DataCollector { // page_file_size_kb libc::sysconf(libc::_SC_PAGESIZE) as u64 / 1024 }, + filters, } } -} -impl DataCollector { pub fn init(&mut self) { #[cfg(target_os = "linux")] { @@ -147,6 +148,7 @@ impl DataCollector { self.sys.refresh_memory(); self.mem_total_kb = self.sys.get_total_memory(); + // TODO: Would be good to get this and network list running on a timer instead...? // Refresh components list once... if self.widgets_to_harvest.use_temp { self.sys.refresh_components_list(); @@ -295,6 +297,7 @@ impl DataCollector { &mut self.total_tx, current_instant, self.widgets_to_harvest.use_net, + &self.filters.net_filter, ) } #[cfg(not(target_os = "windows"))] @@ -305,12 +308,14 @@ impl DataCollector { &mut self.total_tx, current_instant, self.widgets_to_harvest.use_net, + &self.filters.net_filter, ) } }; let mem_data_fut = mem::get_mem_data(self.widgets_to_harvest.use_mem); - let disk_data_fut = disks::get_disk_usage(self.widgets_to_harvest.use_disk); - let disk_io_usage_fut = disks::get_io_usage(false, self.widgets_to_harvest.use_disk); + let disk_data_fut = + disks::get_disk_usage(self.widgets_to_harvest.use_disk, &self.filters.disk_filter); + let disk_io_usage_fut = disks::get_io_usage(self.widgets_to_harvest.use_disk); let temp_data_fut = { #[cfg(not(target_os = "linux"))] { @@ -318,6 +323,7 @@ impl DataCollector { &self.sys, &self.temperature_type, self.widgets_to_harvest.use_temp, + &self.filters.temp_filter, ) } @@ -326,6 +332,7 @@ impl DataCollector { temperature::get_temperature_data( &self.temperature_type, self.widgets_to_harvest.use_temp, + &self.filters.temp_filter, ) } }; diff --git a/src/app/data_harvester/disks.rs b/src/app/data_harvester/disks.rs index 8c447a46..5a67a909 100644 --- a/src/app/data_harvester/disks.rs +++ b/src/app/data_harvester/disks.rs @@ -1,3 +1,5 @@ +use crate::app::Filter; + #[derive(Debug, Clone, Default)] pub struct DiskHarvest { pub name: String, @@ -15,9 +17,7 @@ pub struct IOData { pub type IOHarvest = std::collections::HashMap>; -pub async fn get_io_usage( - get_physical: bool, actually_get: bool, -) -> crate::utils::error::Result> { +pub async fn get_io_usage(actually_get: bool) -> crate::utils::error::Result> { if !actually_get { return Ok(None); } @@ -26,37 +26,23 @@ pub async fn get_io_usage( let mut io_hash: std::collections::HashMap> = std::collections::HashMap::new(); - if get_physical { - let physical_counter_stream = heim::disk::io_counters_physical().await?; - futures::pin_mut!(physical_counter_stream); - while let Some(io) = physical_counter_stream.next().await { - if let Ok(io) = io { - let mount_point = io.device_name().to_str().unwrap_or("Name Unavailable"); - io_hash.insert( - mount_point.to_string(), - Some(IOData { - read_bytes: io.read_bytes().get::(), - write_bytes: io.write_bytes().get::(), - }), - ); - } - } - } else { - let counter_stream = heim::disk::io_counters().await?; - futures::pin_mut!(counter_stream); + let counter_stream = heim::disk::io_counters().await?; + futures::pin_mut!(counter_stream); - while let Some(io) = counter_stream.next().await { - if let Ok(io) = io { - let mount_point = io.device_name().to_str().unwrap_or("Name Unavailable"); - io_hash.insert( - mount_point.to_string(), - Some(IOData { - read_bytes: io.read_bytes().get::(), - write_bytes: io.write_bytes().get::(), - }), - ); - } + while let Some(io) = counter_stream.next().await { + if let Ok(io) = io { + let mount_point = io.device_name().to_str().unwrap_or("Name Unavailable"); + + // FIXME: [MOUNT POINT] Add the filter here I guess? + + io_hash.insert( + mount_point.to_string(), + Some(IOData { + read_bytes: io.read_bytes().get::(), + write_bytes: io.write_bytes().get::(), + }), + ); } } @@ -64,7 +50,7 @@ pub async fn get_io_usage( } pub async fn get_disk_usage( - actually_get: bool, + actually_get: bool, name_filter: &Option, ) -> crate::utils::error::Result>> { if !actually_get { return Ok(None); @@ -77,26 +63,43 @@ pub async fn get_disk_usage( futures::pin_mut!(partitions_stream); while let Some(part) = partitions_stream.next().await { - if let Ok(part) = part { - let partition = part; - let usage = heim::disk::usage(partition.mount_point().to_path_buf()).await?; + if let Ok(partition) = part { + let name = (partition + .device() + .unwrap_or_else(|| std::ffi::OsStr::new("Name Unavailable")) + .to_str() + .unwrap_or("Name Unavailable")) + .to_string(); - vec_disks.push(DiskHarvest { - free_space: usage.free().get::(), - used_space: usage.used().get::(), - total_space: usage.total().get::(), - mount_point: (partition - .mount_point() - .to_str() - .unwrap_or("Name Unavailable")) - .to_string(), - name: (partition - .device() - .unwrap_or_else(|| std::ffi::OsStr::new("Name Unavailable")) - .to_str() - .unwrap_or("Name Unavailable")) - .to_string(), - }); + let mount_point = (partition + .mount_point() + .to_str() + .unwrap_or("Name Unavailable")) + .to_string(); + + let to_keep = if let Some(filter) = name_filter { + let mut ret = filter.is_list_ignored; + for r in &filter.list { + if r.is_match(&name) { + ret = !filter.is_list_ignored; + break; + } + } + ret + } else { + true + }; + + if to_keep { + let usage = heim::disk::usage(partition.mount_point().to_path_buf()).await?; + vec_disks.push(DiskHarvest { + free_space: usage.free().get::(), + used_space: usage.used().get::(), + total_space: usage.total().get::(), + mount_point, + name, + }); + } } } diff --git a/src/app/data_harvester/network.rs b/src/app/data_harvester/network.rs index dcb496cd..55d757f6 100644 --- a/src/app/data_harvester/network.rs +++ b/src/app/data_harvester/network.rs @@ -20,6 +20,7 @@ impl NetworkHarvest { pub async fn get_network_data( sys: &sysinfo::System, prev_net_access_time: Instant, prev_net_rx: &mut u64, prev_net_tx: &mut u64, curr_time: Instant, actually_get: bool, + filter: &Option, ) -> crate::utils::error::Result> { use sysinfo::{NetworkExt, SystemExt}; @@ -31,52 +32,83 @@ pub async fn get_network_data( let mut total_tx: u64 = 0; let networks = sys.get_networks(); - for (_, network) in networks { - total_rx += network.get_total_received(); - total_tx += network.get_total_transmitted(); - } + for (name, network) in networks { + let to_keep = if let Some(filter) = filter { + let mut ret = filter.is_list_ignored; + for r in &filter.list { + if r.is_match(&name) { + ret = !filter.is_list_ignored; + break; + } + } + ret + } else { + true + }; - let elapsed_time = curr_time.duration_since(prev_net_access_time).as_secs_f64(); - - let (rx, tx) = if elapsed_time == 0.0 { - (0, 0) - } else { - ( - ((total_rx.saturating_sub(*prev_net_rx)) as f64 / elapsed_time) as u64, - ((total_tx.saturating_sub(*prev_net_tx)) as f64 / elapsed_time) as u64, - ) - }; - - *prev_net_rx = total_rx; - *prev_net_tx = total_tx; - Ok(Some(NetworkHarvest { - rx, - tx, - total_rx, - total_tx, - })) -} - -#[cfg(not(target_os = "windows"))] -pub async fn get_network_data( - prev_net_access_time: Instant, prev_net_rx: &mut u64, prev_net_tx: &mut u64, - curr_time: Instant, actually_get: bool, -) -> crate::utils::error::Result> { - use futures::StreamExt; - - if !actually_get { - return Ok(None); - } - - let io_data = heim::net::io_counters().await?; - futures::pin_mut!(io_data); - let mut total_rx: u64 = 0; - let mut total_tx: u64 = 0; - - while let Some(io) = io_data.next().await { - if let Ok(io) = io { - total_rx += io.bytes_recv().get::(); - total_tx += io.bytes_sent().get::(); + if to_keep { + total_rx += network.get_total_received(); + total_tx += network.get_total_transmitted(); + } + } + + let elapsed_time = curr_time.duration_since(prev_net_access_time).as_secs_f64(); + + let (rx, tx) = if elapsed_time == 0.0 { + (0, 0) + } else { + ( + ((total_rx.saturating_sub(*prev_net_rx)) as f64 / elapsed_time) as u64, + ((total_tx.saturating_sub(*prev_net_tx)) as f64 / elapsed_time) as u64, + ) + }; + + *prev_net_rx = total_rx; + *prev_net_tx = total_tx; + Ok(Some(NetworkHarvest { + rx, + tx, + total_rx, + total_tx, + })) +} + +// FIXME: Eventually make it so that this thing also takes individual usage into account, so we can allow for showing per-interface! +#[cfg(not(target_os = "windows"))] +pub async fn get_network_data( + prev_net_access_time: Instant, prev_net_rx: &mut u64, prev_net_tx: &mut u64, + curr_time: Instant, actually_get: bool, filter: &Option, +) -> crate::utils::error::Result> { + use futures::StreamExt; + + if !actually_get { + return Ok(None); + } + + let io_data = heim::net::io_counters().await?; + futures::pin_mut!(io_data); + let mut total_rx: u64 = 0; + let mut total_tx: u64 = 0; + + while let Some(io) = io_data.next().await { + if let Ok(io) = io { + let to_keep = if let Some(filter) = filter { + let mut ret = filter.is_list_ignored; + for r in &filter.list { + if r.is_match(&io.interface()) { + ret = !filter.is_list_ignored; + break; + } + } + ret + } else { + true + }; + + if to_keep { + total_rx += io.bytes_recv().get::(); + total_tx += io.bytes_sent().get::(); + } } } diff --git a/src/app/data_harvester/temperature.rs b/src/app/data_harvester/temperature.rs index 512850c9..1c818e31 100644 --- a/src/app/data_harvester/temperature.rs +++ b/src/app/data_harvester/temperature.rs @@ -1,9 +1,10 @@ use std::cmp::Ordering; +use crate::app::Filter; + #[derive(Default, Debug, Clone)] pub struct TempHarvest { - pub component_name: Option, - pub component_label: Option, + pub name: String, pub temperature: f32, } @@ -22,7 +23,7 @@ impl Default for TemperatureType { #[cfg(not(target_os = "linux"))] pub async fn get_temperature_data( - sys: &sysinfo::System, temp_type: &TemperatureType, actually_get: bool, + sys: &sysinfo::System, temp_type: &TemperatureType, actually_get: bool, filter: &Option, ) -> crate::utils::error::Result>> { use sysinfo::{ComponentExt, SystemExt}; @@ -42,17 +43,35 @@ pub async fn get_temperature_data( let sensor_data = sys.get_components(); for component in sensor_data { - temperature_vec.push(TempHarvest { - component_name: None, - component_label: Some(component.get_label().to_string()), - temperature: match temp_type { - TemperatureType::Celsius => component.get_temperature(), - TemperatureType::Kelvin => convert_celsius_to_kelvin(component.get_temperature()), - TemperatureType::Fahrenheit => { - convert_celsius_to_fahrenheit(component.get_temperature()) + let name = component.get_label().to_string(); + + let to_keep = if let Some(filter) = filter { + let mut ret = filter.is_list_ignored; + for r in &filter.list { + if r.is_match(&name) { + ret = !filter.is_list_ignored; + break; } - }, - }); + } + ret + } else { + true + }; + + if to_keep { + temperature_vec.push(TempHarvest { + name, + temperature: match temp_type { + TemperatureType::Celsius => component.get_temperature(), + TemperatureType::Kelvin => { + convert_celsius_to_kelvin(component.get_temperature()) + } + TemperatureType::Fahrenheit => { + convert_celsius_to_fahrenheit(component.get_temperature()) + } + }, + }); + } } temp_vec_sort(&mut temperature_vec); @@ -61,7 +80,7 @@ pub async fn get_temperature_data( #[cfg(target_os = "linux")] pub async fn get_temperature_data( - temp_type: &TemperatureType, actually_get: bool, + temp_type: &TemperatureType, actually_get: bool, filter: &Option, ) -> crate::utils::error::Result>> { use futures::StreamExt; use heim::units::thermodynamic_temperature; @@ -75,26 +94,51 @@ pub async fn get_temperature_data( let mut sensor_data = heim::sensors::temperatures().boxed_local(); while let Some(sensor) = sensor_data.next().await { if let Ok(sensor) = sensor { - temperature_vec.push(TempHarvest { - component_name: Some(sensor.unit().to_string()), - component_label: if let Some(label) = sensor.label() { - Some(label.to_string()) - } else { - None - }, - temperature: match temp_type { - TemperatureType::Celsius => sensor - .current() - .get::(), - TemperatureType::Kelvin => { - sensor.current().get::() + let component_name = Some(sensor.unit().to_string()); + let component_label = if let Some(label) = sensor.label() { + Some(label.to_string()) + } else { + None + }; + + let name = match (component_name, 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(filter) = filter { + let mut ret = filter.is_list_ignored; + for r in &filter.list { + if r.is_match(&name) { + ret = !filter.is_list_ignored; + break; } - TemperatureType::Fahrenheit => sensor - .current() - .get::( - ), - }, - }); + } + ret + } else { + true + }; + + if to_keep { + temperature_vec.push(TempHarvest { + name, + temperature: match temp_type { + TemperatureType::Celsius => sensor + .current() + .get::( + ), + TemperatureType::Kelvin => { + sensor.current().get::() + } + TemperatureType::Fahrenheit => sensor + .current() + .get::( + ), + }, + }); + } } } @@ -116,9 +160,5 @@ fn temp_vec_sort(temperature_vec: &mut Vec) { None => Ordering::Equal, }); - temperature_vec.sort_by(|a, b| { - a.component_name - .partial_cmp(&b.component_name) - .unwrap_or(Ordering::Equal) - }); + temperature_vec.sort_by(|a, b| a.name.partial_cmp(&b.name).unwrap_or(Ordering::Equal)); } diff --git a/src/bin/main.rs b/src/bin/main.rs index 47572bcf..24aacd2f 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -121,6 +121,7 @@ fn main() -> Result<()> { thread_termination_lock.clone(), thread_termination_cvar.clone(), &app.app_config_fields, + app.filters.clone(), app.used_widgets.clone(), ); @@ -192,8 +193,7 @@ fn main() -> Result<()> { // Disk if app.used_widgets.use_disk { - app.canvas_data.disk_data = - convert_disk_row(&app.data_collection, &app.filters.disk_filter); + app.canvas_data.disk_data = convert_disk_row(&app.data_collection); } // Temperatures diff --git a/src/constants.rs b/src/constants.rs index 8f44e427..c50a51d8 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -479,12 +479,21 @@ pub const OLD_CONFIG_TEXT: &str = r##"# This is a default config file for bottom #list = ["/dev/sda\\d+", "/dev/nvme0n1p2"] #regex = true #case_sensitive = false +#whole_word = false #[temp_filter] #is_list_ignored = false #list = ["cpu", "wifi"] #regex = false #case_sensitive = false +#whole_word = false + +#[net_filter] +#is_list_ignored = false +#list = ["virbr0.*"] +#regex = true +#case_sensitive = false +#whole_word = false "##; pub const CONFIG_TOP_HEAD: &str = r##"# This is bottom's config file. diff --git a/src/data_conversion.rs b/src/data_conversion.rs index fb12ba80..c683e62f 100644 --- a/src/data_conversion.rs +++ b/src/data_conversion.rs @@ -2,7 +2,7 @@ //! can actually handle. use crate::Pid; use crate::{ - app::{data_farmer, data_harvester, App, Filter, ProcWidgetState}, + app::{data_farmer, data_harvester, App, ProcWidgetState}, utils::{self, gen_util::*}, }; use data_harvester::processes::ProcessSorting; @@ -84,45 +84,20 @@ pub struct ConvertedCpuData { pub fn convert_temp_row(app: &App) -> Vec> { let current_data = &app.data_collection; let temp_type = &app.app_config_fields.temperature_type; - let temp_filter = &app.filters.temp_filter; let mut sensor_vector: Vec> = 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 - } + .map(|temp_harvest| { + vec![ + temp_harvest.name.clone(), + (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", + }, + ] }) .collect(); @@ -133,26 +108,12 @@ pub fn convert_temp_row(app: &App) -> Vec> { sensor_vector } -pub fn convert_disk_row( - current_data: &data_farmer::DataCollection, disk_filter: &Option, -) -> Vec> { +pub fn convert_disk_row(current_data: &data_farmer::DataCollection) -> Vec> { let mut disk_vector: Vec> = 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(¤t_data.io_labels) .for_each(|(disk, (io_read, io_write))| { let converted_free_space = get_simple_byte_values(disk.free_space, false); @@ -174,6 +135,10 @@ pub fn convert_disk_row( ]); }); + if disk_vector.is_empty() { + disk_vector.push(vec!["No Disks Found".to_string(), "".to_string()]); + } + disk_vector } diff --git a/src/lib.rs b/src/lib.rs index 1f10990e..120c1c10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -620,7 +620,8 @@ pub fn create_collection_thread( >, control_receiver: std::sync::mpsc::Receiver, termination_ctrl_lock: Arc>, termination_ctrl_cvar: Arc, - app_config_fields: &app::AppConfigFields, used_widget_set: UsedWidgets, + app_config_fields: &app::AppConfigFields, filters: app::DataFilters, + used_widget_set: UsedWidgets, ) -> std::thread::JoinHandle<()> { // trace!("Creating collection thread."); let temp_type = app_config_fields.temperature_type.clone(); @@ -630,7 +631,7 @@ pub fn create_collection_thread( thread::spawn(move || { // trace!("Spawned collection thread."); - let mut data_state = data_harvester::DataCollector::default(); + let mut data_state = data_harvester::DataCollector::new(filters); // trace!("Created default data state."); data_state.set_collected_data(used_widget_set); data_state.set_temperature_type(temp_type); diff --git a/src/options.rs b/src/options.rs index 8301094a..999a8cf1 100644 --- a/src/options.rs +++ b/src/options.rs @@ -30,6 +30,7 @@ pub struct Config { pub row: Option>, pub disk_filter: Option, pub temp_filter: Option, + pub net_filter: Option, } impl Config { @@ -216,6 +217,7 @@ pub struct IgnoreList { pub list: Vec, pub regex: Option, pub case_sensitive: Option, + pub whole_word: Option, } pub fn build_app( @@ -413,6 +415,8 @@ pub fn build_app( 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")?; + let net_filter = + get_ignore_list(&config.net_filter).context("Update 'net_filter' in your config file")?; // One more thing - we have to update the search settings of our proc_state_map, and create the hashmaps if needed! // Note that if you change your layout, this might not actually match properly... not sure if/where we should deal with that... @@ -472,6 +476,7 @@ pub fn build_app( .filters(DataFilters { disk_filter, temp_filter, + net_filter, }) .config(config.clone()) .config_path(config_path) @@ -907,17 +912,24 @@ fn get_ignore_list(ignore_list: &Option) -> error::Result