diff --git a/README.md b/README.md index 02974720..823d0b9d 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,9 @@ TBD ### Windows -You may need to install a font like [FreeMono](https://fonts2u.com/free-monospaced.font) and use a terminal like cmder for font support to work properly, unfortunately. I plan to add a Chocolatey install option in the future. +I advise running the program with the `--dot_marker` or `-m` option, as the braille font seems to not work out of the box on Powershell. You may need to install a font like [FreeMono](https://fonts2u.com/free-monospaced.font) and use a terminal like cmder for font support to work properly, unfortunately. + +I plan to add a Chocolatey install option in the future. ### macOS diff --git a/src/app.rs b/src/app.rs index 538a5a81..270aa823 100644 --- a/src/app.rs +++ b/src/app.rs @@ -426,10 +426,8 @@ impl App { self.search_state.current_cursor_position -= 1; } } - } else { - if self.delete_dialog_state.is_showing_dd && !self.delete_dialog_state.is_on_yes { - self.delete_dialog_state.is_on_yes = true; - } + } else if self.delete_dialog_state.is_showing_dd && !self.delete_dialog_state.is_on_yes { + self.delete_dialog_state.is_on_yes = true; } } @@ -442,10 +440,8 @@ impl App { self.search_state.current_cursor_position += 1; } } - } else { - if self.delete_dialog_state.is_showing_dd && self.delete_dialog_state.is_on_yes { - self.delete_dialog_state.is_on_yes = false; - } + } else if self.delete_dialog_state.is_showing_dd && self.delete_dialog_state.is_on_yes { + self.delete_dialog_state.is_on_yes = false; } } @@ -615,14 +611,12 @@ impl App { self.awaiting_second_char = false; } } - } else { - if self.help_dialog_state.is_showing_help { - match caught_char { - '1' => self.help_dialog_state.current_category = AppHelpCategory::General, - '2' => self.help_dialog_state.current_category = AppHelpCategory::Process, - '3' => self.help_dialog_state.current_category = AppHelpCategory::Search, - _ => {} - } + } else if self.help_dialog_state.is_showing_help { + match caught_char { + '1' => self.help_dialog_state.current_category = AppHelpCategory::General, + '2' => self.help_dialog_state.current_category = AppHelpCategory::Process, + '3' => self.help_dialog_state.current_category = AppHelpCategory::Search, + _ => {} } } } diff --git a/src/app/data_harvester/processes.rs b/src/app/data_harvester/processes.rs index 80c81ba2..0092c768 100644 --- a/src/app/data_harvester/processes.rs +++ b/src/app/data_harvester/processes.rs @@ -1,5 +1,9 @@ use crate::utils::error; -use std::{collections::HashMap, process::Command, time::Instant}; +use std::{ + collections::{hash_map::RandomState, HashMap}, + process::Command, + time::Instant, +}; use sysinfo::{ProcessExt, System, SystemExt}; #[derive(Clone)] @@ -111,10 +115,10 @@ fn get_process_cpu_stats(pid: u32) -> std::io::Result { } /// Note that cpu_percentage should be represented WITHOUT the \times 100 factor! -fn linux_cpu_usage( +fn linux_cpu_usage( pid: u32, cpu_usage: f64, cpu_percentage: f64, - prev_pid_stats: &HashMap, - new_pid_stats: &mut HashMap, use_current_cpu_total: bool, + prev_pid_stats: &HashMap, + new_pid_stats: &mut HashMap, use_current_cpu_total: bool, curr_time: &Instant, ) -> std::io::Result { // Based heavily on https://stackoverflow.com/a/23376195 and https://stackoverflow.com/a/1424556 @@ -145,10 +149,10 @@ fn linux_cpu_usage( } } -fn convert_ps( +fn convert_ps( process: &str, cpu_usage: f64, cpu_percentage: f64, - prev_pid_stats: &HashMap, - new_pid_stats: &mut HashMap, use_current_cpu_total: bool, + prev_pid_stats: &HashMap, + new_pid_stats: &mut HashMap, use_current_cpu_total: bool, curr_time: &Instant, ) -> std::io::Result { if process.trim().to_string().is_empty() { @@ -190,7 +194,7 @@ fn convert_ps( pub fn get_sorted_processes_list( sys: &System, prev_idle: &mut f64, prev_non_idle: &mut f64, - prev_pid_stats: &mut HashMap, use_current_cpu_total: bool, + prev_pid_stats: &mut HashMap, use_current_cpu_total: bool, mem_total_kb: u64, curr_time: &Instant, ) -> crate::utils::error::Result> { let mut process_vector: Vec = Vec::new(); @@ -207,7 +211,7 @@ pub fn get_sorted_processes_list( if let Ok((cpu_usage, cpu_percentage)) = cpu_calc { let process_stream = split_string.collect::>(); - let mut new_pid_stats: HashMap = HashMap::new(); + let mut new_pid_stats: HashMap = HashMap::new(); for process in process_stream { if let Ok(process_object) = convert_ps( diff --git a/src/canvas.rs b/src/canvas.rs index 13fb63e2..c90c9829 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -588,7 +588,7 @@ impl Painter { .header_style(self.colours.table_header_style) .widths( &(intrinsic_widths - .into_iter() + .iter() .map(|calculated_width| Constraint::Length(*calculated_width as u16)) .collect::>()), ) @@ -678,7 +678,10 @@ impl Painter { .y_axis(y_axis) .datasets(&[ Dataset::default() - .name("RX") + .name(&format!( + "RX: {:7}", + app_state.canvas_data.rx_display.clone() + )) .marker(if app_state.use_dot { Marker::Dot } else { @@ -687,7 +690,10 @@ impl Painter { .style(self.colours.rx_style) .data(&network_data_rx), Dataset::default() - .name("TX") + .name(&format!( + "TX: {:7}", + app_state.canvas_data.tx_display.clone() + )) .marker(if app_state.use_dot { Marker::Dot } else { @@ -742,7 +748,7 @@ impl Painter { .style(self.colours.text_style) .widths( &(intrinsic_widths - .into_iter() + .iter() .map(|calculated_width| Constraint::Length(*calculated_width as u16)) .collect::>()), ) @@ -809,7 +815,7 @@ impl Painter { .header_style(self.colours.table_header_style) .widths( &(intrinsic_widths - .into_iter() + .iter() .map(|calculated_width| Constraint::Length(*calculated_width as u16)) .collect::>()), ) @@ -876,7 +882,7 @@ impl Painter { .header_style(self.colours.table_header_style) .widths( &(intrinsic_widths - .into_iter() + .iter() .map(|calculated_width| Constraint::Length(*calculated_width as u16)) .collect::>()), ) @@ -1122,7 +1128,7 @@ impl Painter { .header_style(self.colours.table_header_style) .widths( &(intrinsic_widths - .into_iter() + .iter() .map(|calculated_width| Constraint::Length(*calculated_width as u16)) .collect::>()), ) diff --git a/src/canvas/canvas_colours.rs b/src/canvas/canvas_colours.rs index 27a4c4f4..4f40f7b4 100644 --- a/src/canvas/canvas_colours.rs +++ b/src/canvas/canvas_colours.rs @@ -78,11 +78,14 @@ impl CanvasColours { self.tx_style = Style::default().fg(convert_hex_to_color(hex)?); Ok(()) } - pub fn set_cpu_colours(&mut self, hex_colours: &Vec) -> error::Result<()> { + pub fn set_cpu_colours(&mut self, hex_colours: &[String]) -> error::Result<()> { let max_amount = std::cmp::min(hex_colours.len(), NUM_COLOURS as usize); - for i in 0..max_amount { + for (itx, hex_colour) in hex_colours.iter().enumerate() { + if itx >= max_amount { + break; + } self.cpu_colour_styles - .push(Style::default().fg(convert_hex_to_color(&hex_colours[i])?)); + .push(Style::default().fg(convert_hex_to_color(hex_colour)?)); } Ok(()) } diff --git a/src/constants.rs b/src/constants.rs index d01e63ff..791f3809 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -12,7 +12,7 @@ pub const DEFAULT_UNIX_CONFIG_FILE_PATH: &str = "~/.config/btm/btm.toml"; pub const DEFAULT_WINDOWS_CONFIG_FILE_PATH: &str = ""; // Help text -pub const GENERAL_HELP_TEXT: [&'static str; 14] = [ +pub const GENERAL_HELP_TEXT: [&str; 14] = [ "General Keybindings\n\n", "Esc Close dialog box\n", "q, Ctrl-c Quit bottom\n", @@ -29,7 +29,7 @@ pub const GENERAL_HELP_TEXT: [&'static str; 14] = [ "G Skip to the last entry of a list\n", ]; -pub const PROCESS_HELP_TEXT: [&'static str; 8] = [ +pub const PROCESS_HELP_TEXT: [&str; 8] = [ "Process Keybindings\n\n", "dd Kill the highlighted process\n", "c Sort by CPU usage\n", @@ -40,7 +40,7 @@ pub const PROCESS_HELP_TEXT: [&'static str; 8] = [ "Ctrl-f, / Open up the search widget\n", ]; -pub const SEARCH_HELP_TEXT: [&'static str; 8] = [ +pub const SEARCH_HELP_TEXT: [&str; 8] = [ "Search Keybindings\n\n", "Tab Toggle between searching for PID and name.\n", "Esc Close search widget\n", diff --git a/src/main.rs b/src/main.rs index 1ef216d8..40689b38 100644 --- a/src/main.rs +++ b/src/main.rs @@ -53,13 +53,13 @@ enum ResetEvent { Reset, } -#[derive(Deserialize)] +#[derive(Default, Deserialize)] struct Config { flags: Option, colors: Option, } -#[derive(Deserialize)] +#[derive(Default, Deserialize)] struct ConfigFlags { avg_cpu: Option, dot_marker: Option, @@ -73,7 +73,7 @@ struct ConfigFlags { regex: Option, } -#[derive(Deserialize)] +#[derive(Default, Deserialize)] struct ConfigColours { table_header_color: Option, cpu_core_colors: Option>, @@ -90,9 +90,8 @@ struct ConfigColours { graph_color: Option, } -fn main() -> error::Result<()> { - //Parse command line options - let matches = clap_app!(app => +fn get_matches() -> clap::ArgMatches<'static> { + clap_app!(app => (name: crate_name!()) (version: crate_version!()) (author: crate_authors!()) @@ -114,123 +113,24 @@ fn main() -> error::Result<()> { (@arg WHOLE_WORD: -W --whole_word "Match whole word when searching by default.") (@arg REGEX_DEFAULT: -R --regex "Use regex in searching by default.") ) - .get_matches(); + .get_matches() +} - if cfg!(debug_assertions) { - utils::logging::init_logger()?; - } +fn main() -> error::Result<()> { + create_logger()?; + let matches = get_matches(); - let config_path = std::path::Path::new(matches.value_of("CONFIG_LOCATION").unwrap_or( - if cfg!(target_os = "windows") { - DEFAULT_WINDOWS_CONFIG_FILE_PATH - } else { - DEFAULT_UNIX_CONFIG_FILE_PATH - }, - )); + let config: Config = create_config(matches.value_of("CONFIG_LOCATION"))?; - let config_string = std::fs::read_to_string(config_path); - let config_toml: Config = if let Ok(config_str) = config_string { - toml::from_str(&config_str)? - } else { - toml::from_str("")? - }; - - let update_rate_in_milliseconds: u128 = if matches.is_present("RATE_MILLIS") { - matches - .value_of("RATE_MILLIS") - .unwrap_or(&DEFAULT_REFRESH_RATE_IN_MILLISECONDS.to_string()) - .parse::()? - } else if let Some(flags) = &config_toml.flags { - if let Some(rate) = flags.rate { - rate as u128 - } else { - constants::DEFAULT_REFRESH_RATE_IN_MILLISECONDS - } - } else { - constants::DEFAULT_REFRESH_RATE_IN_MILLISECONDS - }; - - if update_rate_in_milliseconds < 250 { - return Err(BottomError::InvalidArg( - "Please set your update rate to be greater than 250 milliseconds.".to_string(), - )); - } else if update_rate_in_milliseconds > u128::from(std::u64::MAX) { - return Err(BottomError::InvalidArg( - "Please set your update rate to be less than unsigned INT_MAX.".to_string(), - )); - } + let update_rate_in_milliseconds: u128 = + get_update_rate_in_milliseconds(&matches.value_of("RATE_MILLIS"), &config)?; // Set other settings - let temperature_type = if matches.is_present("FAHRENHEIT") { - data_harvester::temperature::TemperatureType::Fahrenheit - } else if matches.is_present("KELVIN") { - data_harvester::temperature::TemperatureType::Kelvin - } else if matches.is_present("CELSIUS") { - data_harvester::temperature::TemperatureType::Celsius - } else if let Some(flags) = &config_toml.flags { - if let Some(temp_type) = &flags.temperature_type { - // Give lowest priority to config. - match temp_type.as_str() { - "fahrenheit" | "f" => data_harvester::temperature::TemperatureType::Fahrenheit, - "kelvin" | "k" => data_harvester::temperature::TemperatureType::Kelvin, - "celsius" | "c" => data_harvester::temperature::TemperatureType::Celsius, - _ => { - return Err(BottomError::ConfigError( - "Invalid temperature type. Please have the value be of the form ".to_string() - )); - } - } - } else { - data_harvester::temperature::TemperatureType::Celsius - } - } else { - data_harvester::temperature::TemperatureType::Celsius - }; - let show_average_cpu = if matches.is_present("AVG_CPU") { - true - } else if let Some(flags) = &config_toml.flags { - if let Some(avg_cpu) = flags.avg_cpu { - avg_cpu - } else { - false - } - } else { - false - }; - let use_dot = if matches.is_present("DOT_MARKER") { - true - } else if let Some(flags) = &config_toml.flags { - if let Some(dot_marker) = flags.dot_marker { - dot_marker - } else { - false - } - } else { - false - }; - let left_legend = if matches.is_present("LEFT_LEGEND") { - true - } else if let Some(flags) = &config_toml.flags { - if let Some(left_legend) = flags.left_legend { - left_legend - } else { - false - } - } else { - false - }; - - let use_current_cpu_total = if matches.is_present("USE_CURR_USAGE") { - true - } else if let Some(flags) = &config_toml.flags { - if let Some(current_usage) = flags.current_usage { - current_usage - } else { - false - } - } else { - false - }; + let temperature_type = get_temperature_option(&matches, &config)?; + let show_average_cpu = get_avg_cpu_option(&matches, &config); + let use_dot = get_use_dot_option(&matches, &config); + let left_legend = get_use_left_legend_option(&matches, &config); + let use_current_cpu_total = get_use_current_cpu_total_option(&matches, &config); // Create "app" struct, which will control most of the program and store settings/state let mut app = app::App::new( @@ -242,47 +142,10 @@ fn main() -> error::Result<()> { use_current_cpu_total, ); - // Enable grouping immediately if set. - if matches.is_present("GROUP_PROCESSES") { - app.toggle_grouping(); - } else if let Some(flags) = &config_toml.flags { - if let Some(grouping) = flags.group_processes { - if grouping { - app.toggle_grouping(); - } - } - } - - // Set default search method - if matches.is_present("CASE_SENSITIVE") { - app.search_state.toggle_ignore_case(); - } else if let Some(flags) = &config_toml.flags { - if let Some(case_sensitive) = flags.case_sensitive { - if case_sensitive { - app.search_state.toggle_ignore_case(); - } - } - } - - if matches.is_present("WHOLE_WORD") { - app.search_state.toggle_search_whole_word(); - } else if let Some(flags) = &config_toml.flags { - if let Some(whole_word) = flags.whole_word { - if whole_word { - app.search_state.toggle_search_whole_word(); - } - } - } - - if matches.is_present("REGEX_DEFAULT") { - app.search_state.toggle_search_regex(); - } else if let Some(flags) = &config_toml.flags { - if let Some(regex) = flags.regex { - if regex { - app.search_state.toggle_search_regex(); - } - } - } + enable_app_grouping(&matches, &config, &mut app); + enable_app_case_sensitive(&matches, &config, &mut app); + enable_app_match_whole_word(&matches, &config, &mut app); + enable_app_use_regex(&matches, &config, &mut app); // Set up up tui and crossterm let mut stdout_val = stdout(); @@ -299,37 +162,7 @@ fn main() -> error::Result<()> { // Set up input handling let (tx, rx) = mpsc::channel(); - { - let tx = tx.clone(); - thread::spawn(move || loop { - if poll(Duration::from_millis(20)).is_ok() { - let mut mouse_timer = Instant::now(); - let mut keyboard_timer = Instant::now(); - - loop { - if poll(Duration::from_millis(20)).is_ok() { - if let Ok(event) = read() { - if let CEvent::Key(key) = event { - if Instant::now().duration_since(keyboard_timer).as_millis() >= 20 { - if tx.send(Event::KeyInput(key)).is_err() { - return; - } - keyboard_timer = Instant::now(); - } - } else if let CEvent::Mouse(mouse) = event { - if Instant::now().duration_since(mouse_timer).as_millis() >= 20 { - if tx.send(Event::MouseInput(mouse)).is_err() { - return; - } - mouse_timer = Instant::now(); - } - } - } - } - } - } - }); - } + create_input_thread(tx.clone()); // Cleaning loop { @@ -343,33 +176,16 @@ fn main() -> error::Result<()> { } // Event loop let (rtx, rrx) = mpsc::channel(); - { - let tx = tx; - let temp_type = app.temperature_type.clone(); - thread::spawn(move || { - let tx = tx.clone(); - let mut data_state = data_harvester::DataState::default(); - data_state.init(); - data_state.set_temperature_type(temp_type); - data_state.set_use_current_cpu_total(use_current_cpu_total); - loop { - if let Ok(message) = rrx.try_recv() { - match message { - ResetEvent::Reset => { - data_state.data.first_run_cleanup(); - } - } - } - futures::executor::block_on(data_state.update_data()); - let event = Event::Update(Box::from(data_state.data.clone())); - tx.send(event).unwrap(); - thread::sleep(Duration::from_millis(update_rate_in_milliseconds as u64)); - } - }); - } + create_event_thread( + tx, + rrx, + use_current_cpu_total, + update_rate_in_milliseconds as u64, + app.temperature_type.clone(), + ); let mut painter = canvas::Painter::default(); - if let Err(config_check) = generate_config_colours(&config_toml, &mut painter) { + if let Err(config_check) = generate_config_colours(&config, &mut painter) { cleanup_terminal(&mut terminal)?; return Err(config_check); } @@ -409,10 +225,10 @@ fn main() -> error::Result<()> { match event.code { KeyCode::Char('c') => break, KeyCode::Char('f') => app.enable_searching(), - KeyCode::Left => app.move_left(), - KeyCode::Right => app.move_right(), - KeyCode::Up => app.move_up(), - KeyCode::Down => app.move_down(), + KeyCode::Left | KeyCode::Char('h') => app.move_left(), + KeyCode::Right | KeyCode::Char('l') => app.move_right(), + KeyCode::Up | KeyCode::Char('k') => app.move_up(), + KeyCode::Down | KeyCode::Char('j') => app.move_down(), KeyCode::Char('r') => { if rtx.send(ResetEvent::Reset).is_ok() { app.reset(); @@ -424,10 +240,19 @@ fn main() -> error::Result<()> { } } else if let KeyModifiers::SHIFT = event.modifiers { match event.code { - KeyCode::Left => app.move_left(), - KeyCode::Right => app.move_right(), - KeyCode::Up => app.move_up(), - KeyCode::Down => app.move_down(), + KeyCode::Left | KeyCode::Char('h') | KeyCode::Char('H') => { + app.move_left() + } + KeyCode::Right | KeyCode::Char('l') | KeyCode::Char('L') => { + app.move_right() + } + KeyCode::Up | KeyCode::Char('k') | KeyCode::Char('K') => { + app.move_up() + } + KeyCode::Down | KeyCode::Char('j') | KeyCode::Char('J') => { + app.move_down() + } + KeyCode::Char('/') | KeyCode::Char('?') => app.on_char_key('?'), _ => {} } } else if let KeyModifiers::ALT = event.modifiers { @@ -525,6 +350,189 @@ fn main() -> error::Result<()> { Ok(()) } +fn create_logger() -> error::Result<()> { + if cfg!(debug_assertions) { + utils::logging::init_logger()?; + } + Ok(()) +} + +fn create_config(flag_config_location: Option<&str>) -> error::Result { + let config_path = std::path::Path::new(flag_config_location.unwrap_or( + if cfg!(target_os = "windows") { + DEFAULT_WINDOWS_CONFIG_FILE_PATH + } else { + DEFAULT_UNIX_CONFIG_FILE_PATH + }, + )); + + if let Ok(config_str) = std::fs::read_to_string(config_path) { + Ok(toml::from_str(config_str.as_str())?) + } else { + Ok(Config::default()) + } +} + +fn get_update_rate_in_milliseconds( + update_rate: &Option<&str>, config: &Config, +) -> error::Result { + let update_rate_in_milliseconds = if let Some(update_rate) = update_rate { + update_rate.parse::()? + } else if let Some(flags) = &config.flags { + if let Some(rate) = flags.rate { + rate as u128 + } else { + constants::DEFAULT_REFRESH_RATE_IN_MILLISECONDS + } + } else { + constants::DEFAULT_REFRESH_RATE_IN_MILLISECONDS + }; + + if update_rate_in_milliseconds < 250 { + return Err(BottomError::InvalidArg( + "Please set your update rate to be greater than 250 milliseconds.".to_string(), + )); + } else if update_rate_in_milliseconds > u128::from(std::u64::MAX) { + return Err(BottomError::InvalidArg( + "Please set your update rate to be less than unsigned INT_MAX.".to_string(), + )); + } + + Ok(update_rate_in_milliseconds) +} + +fn get_temperature_option( + matches: &clap::ArgMatches<'static>, config: &Config, +) -> error::Result { + if matches.is_present("FAHRENHEIT") { + return Ok(data_harvester::temperature::TemperatureType::Fahrenheit); + } else if matches.is_present("KELVIN") { + return Ok(data_harvester::temperature::TemperatureType::Kelvin); + } else if matches.is_present("CELSIUS") { + return Ok(data_harvester::temperature::TemperatureType::Celsius); + } else if let Some(flags) = &config.flags { + if let Some(temp_type) = &flags.temperature_type { + // Give lowest priority to config. + match temp_type.as_str() { + "fahrenheit" | "f" => { + return Ok(data_harvester::temperature::TemperatureType::Fahrenheit); + } + "kelvin" | "k" => { + return Ok(data_harvester::temperature::TemperatureType::Kelvin); + } + "celsius" | "c" => { + return Ok(data_harvester::temperature::TemperatureType::Celsius); + } + _ => { + return Err(BottomError::ConfigError( + "Invalid temperature type. Please have the value be of the form ".to_string() + )); + } + } + } + } + Ok(data_harvester::temperature::TemperatureType::Celsius) +} + +fn get_avg_cpu_option(matches: &clap::ArgMatches<'static>, config: &Config) -> bool { + if matches.is_present("AVG_CPU") { + return true; + } else if let Some(flags) = &config.flags { + if let Some(avg_cpu) = flags.avg_cpu { + return avg_cpu; + } + } + + false +} + +fn get_use_dot_option(matches: &clap::ArgMatches<'static>, config: &Config) -> bool { + if matches.is_present("DOT_MARKER") { + return true; + } else if let Some(flags) = &config.flags { + if let Some(dot_marker) = flags.dot_marker { + return dot_marker; + } + } + false +} + +fn get_use_left_legend_option(matches: &clap::ArgMatches<'static>, config: &Config) -> bool { + if matches.is_present("LEFT_LEGEND") { + return true; + } else if let Some(flags) = &config.flags { + if let Some(left_legend) = flags.left_legend { + return left_legend; + } + } + + false +} + +fn get_use_current_cpu_total_option(matches: &clap::ArgMatches<'static>, config: &Config) -> bool { + if matches.is_present("USE_CURR_USAGE") { + return true; + } else if let Some(flags) = &config.flags { + if let Some(current_usage) = flags.current_usage { + return current_usage; + } + } + + false +} + +fn enable_app_grouping(matches: &clap::ArgMatches<'static>, config: &Config, app: &mut app::App) { + if matches.is_present("GROUP_PROCESSES") { + app.toggle_grouping(); + } else if let Some(flags) = &config.flags { + if let Some(grouping) = flags.group_processes { + if grouping { + app.toggle_grouping(); + } + } + } +} + +fn enable_app_case_sensitive( + matches: &clap::ArgMatches<'static>, config: &Config, app: &mut app::App, +) { + if matches.is_present("CASE_SENSITIVE") { + app.search_state.toggle_ignore_case(); + } else if let Some(flags) = &config.flags { + if let Some(case_sensitive) = flags.case_sensitive { + if case_sensitive { + app.search_state.toggle_ignore_case(); + } + } + } +} + +fn enable_app_match_whole_word( + matches: &clap::ArgMatches<'static>, config: &Config, app: &mut app::App, +) { + if matches.is_present("WHOLE_WORD") { + app.search_state.toggle_search_whole_word(); + } else if let Some(flags) = &config.flags { + if let Some(whole_word) = flags.whole_word { + if whole_word { + app.search_state.toggle_search_whole_word(); + } + } + } +} + +fn enable_app_use_regex(matches: &clap::ArgMatches<'static>, config: &Config, app: &mut app::App) { + if matches.is_present("REGEX_DEFAULT") { + app.search_state.toggle_search_regex(); + } else if let Some(flags) = &config.flags { + if let Some(regex) = flags.regex { + if regex { + app.search_state.toggle_search_regex(); + } + } + } +} + fn try_drawing( terminal: &mut tui::terminal::Terminal>, app: &mut app::App, painter: &mut canvas::Painter, @@ -549,10 +557,8 @@ fn cleanup_terminal( Ok(()) } -fn generate_config_colours( - config_toml: &Config, painter: &mut canvas::Painter, -) -> error::Result<()> { - if let Some(colours) = &config_toml.colors { +fn generate_config_colours(config: &Config, painter: &mut canvas::Painter) -> error::Result<()> { + if let Some(colours) = &config.colors { if let Some(border_color) = &colours.border_color { painter.colours.set_border_colour(border_color)?; } @@ -720,3 +726,63 @@ fn sort_process_data(to_sort_vec: &mut Vec, app: &app::App } } } + +fn create_input_thread( + tx: std::sync::mpsc::Sender>, +) { + thread::spawn(move || loop { + if poll(Duration::from_millis(20)).is_ok() { + let mut mouse_timer = Instant::now(); + let mut keyboard_timer = Instant::now(); + + loop { + if poll(Duration::from_millis(20)).is_ok() { + if let Ok(event) = read() { + if let CEvent::Key(key) = event { + if Instant::now().duration_since(keyboard_timer).as_millis() >= 20 { + if tx.send(Event::KeyInput(key)).is_err() { + return; + } + keyboard_timer = Instant::now(); + } + } else if let CEvent::Mouse(mouse) = event { + if Instant::now().duration_since(mouse_timer).as_millis() >= 20 { + if tx.send(Event::MouseInput(mouse)).is_err() { + return; + } + mouse_timer = Instant::now(); + } + } + } + } + } + } + }); +} + +fn create_event_thread( + tx: std::sync::mpsc::Sender>, + rrx: std::sync::mpsc::Receiver, use_current_cpu_total: bool, + update_rate_in_milliseconds: u64, temp_type: data_harvester::temperature::TemperatureType, +) { + thread::spawn(move || { + let tx = tx.clone(); + let mut data_state = data_harvester::DataState::default(); + data_state.init(); + data_state.set_temperature_type(temp_type); + data_state.set_use_current_cpu_total(use_current_cpu_total); + loop { + if let Ok(message) = rrx.try_recv() { + match message { + ResetEvent::Reset => { + data_state.data.first_run_cleanup(); + } + } + } + futures::executor::block_on(data_state.update_data()); + let event = Event::Update(Box::from(data_state.data.clone())); + tx.send(event).unwrap(); + thread::sleep(Duration::from_millis(update_rate_in_milliseconds)); + } + }); +}