From 3026fbd1bc6ec3d757dba6ad30428812b63190c9 Mon Sep 17 00:00:00 2001 From: ClementTsang Date: Sun, 8 Mar 2020 19:47:10 -0400 Subject: [PATCH] Add time scaling. --- src/app.rs | 122 +++++++++++++++++++++++----- src/app/data_farmer.rs | 6 ++ src/canvas.rs | 4 +- src/canvas/widgets/cpu_graph.rs | 13 ++- src/canvas/widgets/mem_graph.rs | 11 ++- src/canvas/widgets/network_graph.rs | 12 ++- src/constants.rs | 9 +- src/data_conversion.rs | 121 +++++++++++++++++++-------- src/main.rs | 73 +++++++++++++++-- 9 files changed, 299 insertions(+), 72 deletions(-) diff --git a/src/app.rs b/src/app.rs index 4d536262..8e11f1fa 100644 --- a/src/app.rs +++ b/src/app.rs @@ -227,6 +227,8 @@ pub struct NetworkState { pub is_showing_rx: bool, pub is_showing_tx: bool, pub zoom_level: f64, + pub display_time: u128, + pub force_update: bool, } impl Default for NetworkState { @@ -236,6 +238,8 @@ impl Default for NetworkState { is_showing_rx: true, is_showing_tx: true, zoom_level: 100.0, + display_time: constants::DEFAULT_DISPLAY_MILLISECONDS, + force_update: false, } } } @@ -245,6 +249,8 @@ pub struct CpuState { pub is_showing_tray: bool, pub zoom_level: f64, pub core_show_vec: Vec, + pub display_time: u128, + pub force_update: bool, } impl Default for CpuState { @@ -253,6 +259,8 @@ impl Default for CpuState { is_showing_tray: false, zoom_level: 100.0, core_show_vec: Vec::new(), + display_time: constants::DEFAULT_DISPLAY_MILLISECONDS, + force_update: false, } } } @@ -263,6 +271,8 @@ pub struct MemState { pub is_showing_ram: bool, pub is_showing_swap: bool, pub zoom_level: f64, + pub display_time: u128, + pub force_update: bool, } impl Default for MemState { @@ -272,6 +282,8 @@ impl Default for MemState { is_showing_ram: true, is_showing_swap: true, zoom_level: 100.0, + display_time: constants::DEFAULT_DISPLAY_MILLISECONDS, + force_update: false, } } } @@ -279,7 +291,7 @@ impl Default for MemState { pub struct App { pub process_sorting_type: processes::ProcessSorting, pub process_sorting_reverse: bool, - pub update_process_gui: bool, + pub force_update_processes: bool, pub app_scroll_positions: AppScrollState, pub current_widget_selected: WidgetPosition, pub previous_basic_table_selected: WidgetPosition, @@ -305,6 +317,7 @@ pub struct App { impl App { #[allow(clippy::too_many_arguments)] + // TODO: [REFACTOR] use builder pattern instead. pub fn new( show_average_cpu: bool, temperature_type: temperature::TemperatureType, update_rate_in_milliseconds: u128, use_dot: bool, left_legend: bool, @@ -314,7 +327,7 @@ impl App { App { process_sorting_type: processes::ProcessSorting::CPU, process_sorting_reverse: true, - update_process_gui: false, + force_update_processes: false, current_widget_selected: if use_basic_mode { match current_widget_selected { WidgetPosition::Cpu => WidgetPosition::BasicCpu, @@ -443,7 +456,7 @@ impl App { if !self.is_in_dialog() { if let WidgetPosition::Process = self.current_widget_selected { self.enable_grouping = !(self.enable_grouping); - self.update_process_gui = true; + self.force_update_processes = true; } } } @@ -455,7 +468,7 @@ impl App { if self.is_grouped() { self.search_with_name(); } else { - self.update_process_gui = true; + self.force_update_processes = true; } } WidgetPosition::ProcessSearch => { @@ -539,14 +552,14 @@ impl App { pub fn search_with_pid(&mut self) { if !self.is_in_dialog() && self.is_searching() { self.process_search_state.is_searching_with_pid = true; - self.update_process_gui = true; + self.force_update_processes = true; } } pub fn search_with_name(&mut self) { if !self.is_in_dialog() && self.is_searching() { self.process_search_state.is_searching_with_pid = false; - self.update_process_gui = true; + self.force_update_processes = true; } } @@ -557,19 +570,19 @@ impl App { pub fn toggle_ignore_case(&mut self) { self.process_search_state.search_toggle_ignore_case(); self.update_regex(); - self.update_process_gui = true; + self.force_update_processes = true; } pub fn toggle_search_whole_word(&mut self) { self.process_search_state.search_toggle_whole_word(); self.update_regex(); - self.update_process_gui = true; + self.force_update_processes = true; } pub fn toggle_search_regex(&mut self) { self.process_search_state.search_toggle_regex(); self.update_regex(); - self.update_process_gui = true; + self.force_update_processes = true; } pub fn update_regex(&mut self) { @@ -703,7 +716,7 @@ impl App { ); self.update_regex(); - self.update_process_gui = true; + self.force_update_processes = true; } } _ => {} @@ -720,7 +733,7 @@ impl App { pub fn clear_search(&mut self) { if let WidgetPosition::ProcessSearch = self.current_widget_selected { - self.update_process_gui = true; + self.force_update_processes = true; self.process_search_state.search_state = AppSearchState::reset(); } } @@ -772,7 +785,7 @@ impl App { self.process_search_state.search_state.cursor_direction = CursorDirection::LEFT; self.update_regex(); - self.update_process_gui = true; + self.force_update_processes = true; } } } @@ -967,7 +980,7 @@ impl App { UnicodeWidthChar::width(caught_char).unwrap_or(0); self.update_regex(); - self.update_process_gui = true; + self.force_update_processes = true; self.process_search_state.search_state.cursor_direction = CursorDirection::RIGHT; } @@ -1016,6 +1029,9 @@ impl App { 'j' => self.increment_position_count(), 'f' => { self.is_frozen = !self.is_frozen; + if self.is_frozen { + self.data_collection.set_frozen_time(); + } } 'c' => { match self.process_sorting_type { @@ -1027,7 +1043,7 @@ impl App { self.process_sorting_reverse = true; } } - self.update_process_gui = true; + self.force_update_processes = true; self.app_scroll_positions .process_scroll_state .current_scroll_position = 0; @@ -1042,7 +1058,7 @@ impl App { self.process_sorting_reverse = true; } } - self.update_process_gui = true; + self.force_update_processes = true; self.app_scroll_positions .process_scroll_state .current_scroll_position = 0; @@ -1059,7 +1075,7 @@ impl App { self.process_sorting_reverse = false; } } - self.update_process_gui = true; + self.force_update_processes = true; self.app_scroll_positions .process_scroll_state .current_scroll_position = 0; @@ -1075,7 +1091,7 @@ impl App { self.process_sorting_reverse = false; } } - self.update_process_gui = true; + self.force_update_processes = true; self.app_scroll_positions .process_scroll_state .current_scroll_position = 0; @@ -1088,9 +1104,9 @@ impl App { 'K' => self.move_widget_selection_up(), 'J' => self.move_widget_selection_down(), ' ' => self.on_space(), - '+' => {} - '-' => {} - '=' => {} + '+' => self.zoom_in(), + '-' => self.zoom_out(), + '=' => self.reset_zoom(), _ => {} } @@ -1470,7 +1486,69 @@ impl App { } } - fn zoom_out(&mut self) {} + fn zoom_out(&mut self) { + match self.current_widget_selected { + WidgetPosition::Cpu => { + if self.cpu_state.display_time < constants::STALE_MAX_MILLISECONDS { + self.cpu_state.display_time += constants::TIME_CHANGE_MILLISECONDS; + self.cpu_state.force_update = true; + } + } + WidgetPosition::Mem => { + if self.mem_state.display_time < constants::STALE_MAX_MILLISECONDS { + self.mem_state.display_time += constants::TIME_CHANGE_MILLISECONDS; + self.mem_state.force_update = true; + } + } + WidgetPosition::Network => { + if self.net_state.display_time < constants::STALE_MAX_MILLISECONDS { + self.net_state.display_time += constants::TIME_CHANGE_MILLISECONDS; + self.net_state.force_update = true; + } + } + _ => {} + } + } - fn zoom_in(&mut self) {} + fn zoom_in(&mut self) { + match self.current_widget_selected { + WidgetPosition::Cpu => { + if self.cpu_state.display_time > constants::STALE_MIN_MILLISECONDS { + self.cpu_state.display_time -= constants::TIME_CHANGE_MILLISECONDS; + self.cpu_state.force_update = true; + } + } + WidgetPosition::Mem => { + if self.mem_state.display_time > constants::STALE_MIN_MILLISECONDS { + self.mem_state.display_time -= constants::TIME_CHANGE_MILLISECONDS; + self.mem_state.force_update = true; + } + } + WidgetPosition::Network => { + if self.net_state.display_time > constants::STALE_MIN_MILLISECONDS { + self.net_state.display_time -= constants::TIME_CHANGE_MILLISECONDS; + self.net_state.force_update = true; + } + } + _ => {} + } + } + + fn reset_zoom(&mut self) { + match self.current_widget_selected { + WidgetPosition::Cpu => { + self.cpu_state.display_time = constants::DEFAULT_DISPLAY_MILLISECONDS; + self.cpu_state.force_update = true; + } + WidgetPosition::Mem => { + self.mem_state.display_time = constants::DEFAULT_DISPLAY_MILLISECONDS; + self.mem_state.force_update = true; + } + WidgetPosition::Network => { + self.net_state.display_time = constants::DEFAULT_DISPLAY_MILLISECONDS; + self.net_state.force_update = true; + } + _ => {} + } + } } diff --git a/src/app/data_farmer.rs b/src/app/data_farmer.rs index e8e98160..d3d77ca2 100644 --- a/src/app/data_farmer.rs +++ b/src/app/data_farmer.rs @@ -45,6 +45,7 @@ pub struct TimedData { #[derive(Debug)] pub struct DataCollection { pub current_instant: Instant, + pub frozen_instant: Option, pub timed_data_vec: Vec<(Instant, TimedData)>, pub network_harvest: network::NetworkHarvest, pub memory_harvest: mem::MemHarvest, @@ -62,6 +63,7 @@ impl Default for DataCollection { fn default() -> Self { DataCollection { current_instant: Instant::now(), + frozen_instant: None, timed_data_vec: Vec::default(), network_harvest: network::NetworkHarvest::default(), memory_harvest: mem::MemHarvest::default(), @@ -78,6 +80,10 @@ impl Default for DataCollection { } impl DataCollection { + pub fn set_frozen_time(&mut self) { + self.frozen_instant = Some(self.current_instant); + } + pub fn clean_data(&mut self, max_time_millis: u128) { let current_time = Instant::now(); diff --git a/src/canvas.rs b/src/canvas.rs index 468ac70a..aa657ab2 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -35,12 +35,12 @@ pub struct DisplayableData { pub network_data_tx: Vec<(f64, f64)>, pub disk_data: Vec>, pub temp_sensor_data: Vec>, + // Not the final value pub process_data: HashMap, // Not the final value pub grouped_process_data: Vec, - // Not the final value - pub finalized_process_data: Vec, // What's actually displayed + pub finalized_process_data: Vec, pub mem_label: String, pub swap_label: String, pub mem_data: Vec<(f64, f64)>, diff --git a/src/canvas/widgets/cpu_graph.rs b/src/canvas/widgets/cpu_graph.rs index e9f4ddcf..59cee390 100644 --- a/src/canvas/widgets/cpu_graph.rs +++ b/src/canvas/widgets/cpu_graph.rs @@ -41,8 +41,17 @@ impl CpuGraphWidget for Painter { fn draw_cpu_graph(&self, f: &mut Frame<'_, B>, app_state: &App, draw_loc: Rect) { let cpu_data: &[ConvertedCpuData] = &app_state.canvas_data.cpu_data; - // CPU usage graph - let x_axis: Axis<'_, String> = Axis::default().bounds([0.0, TIME_STARTS_FROM as f64]); + let display_time_labels = [ + format!("{}s", app_state.cpu_state.display_time / 1000), + "0s".to_string(), + ]; + let x_axis = Axis::default() + .bounds([0.0, app_state.cpu_state.display_time as f64]) + .style(self.colours.graph_style) + .labels_style(self.colours.graph_style) + .labels(&display_time_labels); + + // Note this is offset as otherwise the 0 value is not drawn! let y_axis = Axis::default() .style(self.colours.graph_style) .labels_style(self.colours.graph_style) diff --git a/src/canvas/widgets/mem_graph.rs b/src/canvas/widgets/mem_graph.rs index f16f9e9e..58ca8545 100644 --- a/src/canvas/widgets/mem_graph.rs +++ b/src/canvas/widgets/mem_graph.rs @@ -3,7 +3,6 @@ use std::cmp::max; use crate::{ app::{App, WidgetPosition}, canvas::Painter, - constants::*, }; use tui::{ @@ -22,7 +21,15 @@ impl MemGraphWidget for Painter { let mem_data: &[(f64, f64)] = &app_state.canvas_data.mem_data; let swap_data: &[(f64, f64)] = &app_state.canvas_data.swap_data; - let x_axis: Axis<'_, String> = Axis::default().bounds([0.0, TIME_STARTS_FROM as f64]); + let display_time_labels = [ + format!("{}s", app_state.mem_state.display_time / 1000), + "0s".to_string(), + ]; + let x_axis = Axis::default() + .bounds([0.0, app_state.mem_state.display_time as f64]) + .style(self.colours.graph_style) + .labels_style(self.colours.graph_style) + .labels(&display_time_labels); // Offset as the zero value isn't drawn otherwise... let y_axis: Axis<'_, &str> = Axis::default() diff --git a/src/canvas/widgets/network_graph.rs b/src/canvas/widgets/network_graph.rs index 50c829a7..c22f3847 100644 --- a/src/canvas/widgets/network_graph.rs +++ b/src/canvas/widgets/network_graph.rs @@ -37,7 +37,17 @@ impl NetworkGraphWidget for Painter { let network_data_rx: &[(f64, f64)] = &app_state.canvas_data.network_data_rx; let network_data_tx: &[(f64, f64)] = &app_state.canvas_data.network_data_tx; - let x_axis: Axis<'_, String> = Axis::default().bounds([0.0, 60_000.0]); + let display_time_labels = [ + format!("{}s", app_state.net_state.display_time / 1000), + "0s".to_string(), + ]; + let x_axis = Axis::default() + .bounds([0.0, app_state.net_state.display_time as f64]) + .style(self.colours.graph_style) + .labels_style(self.colours.graph_style) + .labels(&display_time_labels); + + // 0 is offset. let y_axis: Axis<'_, &str> = Axis::default() .style(self.colours.graph_style) .labels_style(self.colours.graph_style) diff --git a/src/constants.rs b/src/constants.rs index c8f1c068..b177d141 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1,6 +1,11 @@ // How long to store data. -pub const STALE_MAX_MILLISECONDS: u128 = 60 * 1000; -pub const TIME_STARTS_FROM: u64 = 60 * 1000; +pub const STALE_MAX_MILLISECONDS: u128 = 300 * 1000; // Keep 5 minutes of data. + +// How much data is SHOWN +pub const DEFAULT_DISPLAY_MILLISECONDS: u128 = 60 * 1000; // Defaults to 1 min. +pub const STALE_MIN_MILLISECONDS: u128 = 30 * 1000; // Lowest is 30 seconds +pub const TIME_CHANGE_MILLISECONDS: u128 = 15 * 1000; // How much to increment each time + pub const TICK_RATE_IN_MILLISECONDS: u64 = 200; // How fast the screen refreshes pub const DEFAULT_REFRESH_RATE_IN_MILLISECONDS: u128 = 1000; diff --git a/src/data_conversion.rs b/src/data_conversion.rs index 9191e378..341ada48 100644 --- a/src/data_conversion.rs +++ b/src/data_conversion.rs @@ -3,22 +3,21 @@ use std::collections::HashMap; -use constants::*; - use crate::{ app::{ data_farmer, data_harvester::{self, processes::ProcessHarvest}, App, }, - constants, utils::gen_util::{get_exact_byte_values, get_simple_byte_values}, }; +type Point = (f64, f64); + #[derive(Default, Debug)] pub struct ConvertedNetworkData { - pub rx: Vec<(f64, f64)>, - pub tx: Vec<(f64, f64)>, + pub rx: Vec, + pub tx: Vec, pub rx_display: String, pub tx_display: String, pub total_rx_display: String, @@ -38,7 +37,7 @@ pub struct ConvertedProcessData { pub struct ConvertedCpuData { pub cpu_name: String, /// Tuple is time, value - pub cpu_data: Vec<(f64, f64)>, + pub cpu_data: Vec, } pub fn convert_temp_row(app: &App) -> Vec> { @@ -103,16 +102,24 @@ pub fn convert_disk_row(current_data: &data_farmer::DataCollection) -> Vec Vec { let mut cpu_data_vector: Vec = Vec::new(); - let current_time = current_data.current_instant; + let current_time = if is_frozen { + if let Some(frozen_instant) = current_data.frozen_instant { + frozen_instant + } else { + current_data.current_instant + } + } else { + current_data.current_instant + }; let cpu_listing_offset = if show_avg_cpu { 0 } else { 1 }; for (time, data) in ¤t_data.timed_data_vec { - let time_from_start: f64 = (TIME_STARTS_FROM as f64 - - current_time.duration_since(*time).as_millis() as f64) - .floor(); + let time_from_start: f64 = + (display_time as f64 - current_time.duration_since(*time).as_millis() as f64).floor(); for (itx, cpu) in data.cpu_data.iter().enumerate() { if !show_avg_cpu && itx == 0 { @@ -139,19 +146,32 @@ pub fn convert_cpu_data_points( .cpu_data .push((time_from_start, cpu.0)); } + + if *time == current_time { + break; + } } cpu_data_vector } -pub fn convert_mem_data_points(current_data: &data_farmer::DataCollection) -> Vec<(f64, f64)> { - let mut result: Vec<(f64, f64)> = Vec::new(); - let current_time = current_data.current_instant; +pub fn convert_mem_data_points( + current_data: &data_farmer::DataCollection, display_time: u128, is_frozen: bool, +) -> Vec { + let mut result: Vec = Vec::new(); + let current_time = if is_frozen { + if let Some(frozen_instant) = current_data.frozen_instant { + frozen_instant + } else { + current_data.current_instant + } + } else { + current_data.current_instant + }; for (time, data) in ¤t_data.timed_data_vec { - let time_from_start: f64 = (TIME_STARTS_FROM as f64 - - current_time.duration_since(*time).as_millis() as f64) - .floor(); + let time_from_start: f64 = + (display_time as f64 - current_time.duration_since(*time).as_millis() as f64).floor(); //Insert joiner points for &(joiner_offset, joiner_val) in &data.mem_data.1 { @@ -160,19 +180,32 @@ pub fn convert_mem_data_points(current_data: &data_farmer::DataCollection) -> Ve } result.push((time_from_start, data.mem_data.0)); + + if *time == current_time { + break; + } } result } -pub fn convert_swap_data_points(current_data: &data_farmer::DataCollection) -> Vec<(f64, f64)> { - let mut result: Vec<(f64, f64)> = Vec::new(); - let current_time = current_data.current_instant; +pub fn convert_swap_data_points( + current_data: &data_farmer::DataCollection, display_time: u128, is_frozen: bool, +) -> Vec { + let mut result: Vec = Vec::new(); + let current_time = if is_frozen { + if let Some(frozen_instant) = current_data.frozen_instant { + frozen_instant + } else { + current_data.current_instant + } + } else { + current_data.current_instant + }; for (time, data) in ¤t_data.timed_data_vec { - let time_from_start: f64 = (TIME_STARTS_FROM as f64 - - current_time.duration_since(*time).as_millis() as f64) - .floor(); + let time_from_start: f64 = + (display_time as f64 - current_time.duration_since(*time).as_millis() as f64).floor(); //Insert joiner points for &(joiner_offset, joiner_val) in &data.swap_data.1 { @@ -181,6 +214,10 @@ pub fn convert_swap_data_points(current_data: &data_farmer::DataCollection) -> V } result.push((time_from_start, data.swap_data.0)); + + if *time == current_time { + break; + } } result @@ -222,17 +259,25 @@ pub fn convert_mem_labels(current_data: &data_farmer::DataCollection) -> (String (mem_label, swap_label) } -pub fn convert_network_data_points( - current_data: &data_farmer::DataCollection, -) -> ConvertedNetworkData { - let mut rx: Vec<(f64, f64)> = Vec::new(); - let mut tx: Vec<(f64, f64)> = Vec::new(); +pub fn get_rx_tx_data_points( + current_data: &data_farmer::DataCollection, display_time: u128, is_frozen: bool, +) -> (Vec, Vec) { + let mut rx: Vec = Vec::new(); + let mut tx: Vec = Vec::new(); + + let current_time = if is_frozen { + if let Some(frozen_instant) = current_data.frozen_instant { + frozen_instant + } else { + current_data.current_instant + } + } else { + current_data.current_instant + }; - let current_time = current_data.current_instant; for (time, data) in ¤t_data.timed_data_vec { - let time_from_start: f64 = (TIME_STARTS_FROM as f64 - - current_time.duration_since(*time).as_millis() as f64) - .floor(); + let time_from_start: f64 = + (display_time as f64 - current_time.duration_since(*time).as_millis() as f64).floor(); //Insert joiner points for &(joiner_offset, joiner_val) in &data.rx_data.1 { @@ -247,8 +292,20 @@ pub fn convert_network_data_points( rx.push((time_from_start, data.rx_data.0)); tx.push((time_from_start, data.tx_data.0)); + + if *time == current_time { + break; + } } + (rx, tx) +} + +pub fn convert_network_data_points( + current_data: &data_farmer::DataCollection, display_time: u128, is_frozen: bool, +) -> ConvertedNetworkData { + let (rx, tx) = get_rx_tx_data_points(current_data, display_time, is_frozen); + let total_rx_converted_result: (f64, String); let rx_converted_result: (f64, String); let total_tx_converted_result: (f64, String); diff --git a/src/main.rs b/src/main.rs index 031a1d57..adf12e7d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -187,13 +187,12 @@ fn main() -> error::Result<()> { if handle_key_event_or_break(event, &mut app, &rtx) { break; } - - if app.update_process_gui { - update_final_process_list(&mut app); - app.update_process_gui = false; - } + handle_force_redraws(&mut app); + } + BottomEvent::MouseInput(event) => { + handle_mouse_event(event, &mut app); + handle_force_redraws(&mut app); } - BottomEvent::MouseInput(event) => handle_mouse_event(event, &mut app), BottomEvent::Update(data) => { app.data_collection.eat_data(&data); @@ -201,7 +200,11 @@ fn main() -> error::Result<()> { // Convert all data into tui-compliant components // Network - let network_data = convert_network_data_points(&app.data_collection); + let network_data = convert_network_data_points( + &app.data_collection, + app.net_state.display_time, + false, + ); app.canvas_data.network_data_rx = network_data.rx; app.canvas_data.network_data_tx = network_data.tx; app.canvas_data.rx_display = network_data.rx_display; @@ -215,8 +218,16 @@ fn main() -> error::Result<()> { // Temperatures app.canvas_data.temp_sensor_data = convert_temp_row(&app); // Memory - app.canvas_data.mem_data = convert_mem_data_points(&app.data_collection); - app.canvas_data.swap_data = convert_swap_data_points(&app.data_collection); + app.canvas_data.mem_data = convert_mem_data_points( + &app.data_collection, + app.mem_state.display_time, + false, + ); + app.canvas_data.swap_data = convert_swap_data_points( + &app.data_collection, + app.mem_state.display_time, + false, + ); let memory_and_swap_labels = convert_mem_labels(&app.data_collection); app.canvas_data.mem_label = memory_and_swap_labels.0; app.canvas_data.swap_label = memory_and_swap_labels.1; @@ -225,6 +236,8 @@ fn main() -> error::Result<()> { app.canvas_data.cpu_data = convert_cpu_data_points( app.app_config_fields.show_average_cpu, &app.data_collection, + app.cpu_state.display_time, + false, ); // Pre-fill CPU if needed @@ -560,6 +573,48 @@ fn panic_hook(panic_info: &PanicInfo<'_>) { .unwrap(); } +fn handle_force_redraws(app: &mut App) { + if app.force_update_processes { + update_final_process_list(app); + app.force_update_processes = false; + } + + if app.cpu_state.force_update { + app.canvas_data.cpu_data = convert_cpu_data_points( + app.app_config_fields.show_average_cpu, + &app.data_collection, + app.cpu_state.display_time, + app.is_frozen, + ); + app.cpu_state.force_update = false; + } + + if app.mem_state.force_update { + app.canvas_data.mem_data = convert_mem_data_points( + &app.data_collection, + app.mem_state.display_time, + app.is_frozen, + ); + app.canvas_data.swap_data = convert_swap_data_points( + &app.data_collection, + app.mem_state.display_time, + app.is_frozen, + ); + app.mem_state.force_update = false; + } + + if app.net_state.force_update { + let (rx, tx) = get_rx_tx_data_points( + &app.data_collection, + app.net_state.display_time, + app.is_frozen, + ); + app.canvas_data.network_data_rx = rx; + app.canvas_data.network_data_tx = tx; + app.net_state.force_update = false; + } +} + fn update_final_process_list(app: &mut App) { let mut filtered_process_data: Vec = if app.is_grouped() { app.canvas_data