diff --git a/src/app/data_farmer.rs b/src/app/data_farmer.rs index af75d1de6..28801bb4f 100644 --- a/src/app/data_farmer.rs +++ b/src/app/data_farmer.rs @@ -124,7 +124,7 @@ pub struct DataCollection { pub io_labels: Vec<(String, String)>, pub temp_harvest: Vec, #[cfg(feature = "battery")] - pub battery_harvest: Vec, + pub battery_harvest: Vec, #[cfg(feature = "zfs")] pub arc_harvest: memory::MemHarvest, #[cfg(feature = "gpu")] @@ -451,7 +451,7 @@ impl DataCollection { } #[cfg(feature = "battery")] - fn eat_battery(&mut self, list_of_batteries: Vec) { + fn eat_battery(&mut self, list_of_batteries: Vec) { self.battery_harvest = list_of_batteries; } diff --git a/src/app/states.rs b/src/app/states.rs index 821bf4728..26b2462a2 100644 --- a/src/app/states.rs +++ b/src/app/states.rs @@ -21,7 +21,7 @@ pub struct AppWidgetStates { pub proc_state: ProcState, pub temp_state: TempState, pub disk_state: DiskState, - pub battery_state: BatteryState, + pub battery_state: AppBatteryState, pub basic_table_widget_state: Option, } @@ -381,13 +381,13 @@ pub struct BasicTableWidgetState { pub right_brc: Option<(u16, u16)>, } -pub struct BatteryState { +pub struct AppBatteryState { pub widget_states: HashMap, } -impl BatteryState { +impl AppBatteryState { pub fn init(widget_states: HashMap) -> Self { - BatteryState { widget_states } + AppBatteryState { widget_states } } pub fn get_mut_widget_state(&mut self, widget_id: u64) -> Option<&mut BatteryWidgetState> { diff --git a/src/canvas/components/data_table/draw.rs b/src/canvas/components/data_table/draw.rs index 8dc39c81a..617c76118 100644 --- a/src/canvas/components/data_table/draw.rs +++ b/src/canvas/components/data_table/draw.rs @@ -133,11 +133,10 @@ where &mut self, f: &mut Frame<'_>, draw_info: &DrawInfo, widget: Option<&mut BottomWidget>, painter: &Painter, ) { - let draw_horizontal = !self.props.is_basic || draw_info.is_on_widget(); let draw_loc = draw_info.loc; let margined_draw_loc = Layout::default() .constraints([Constraint::Percentage(100)]) - .horizontal_margin(u16::from(!draw_horizontal)) + .horizontal_margin(u16::from(self.props.is_basic && !draw_info.is_on_widget())) .direction(Direction::Horizontal) .split(draw_loc)[0]; diff --git a/src/canvas/widgets/battery_display.rs b/src/canvas/widgets/battery_display.rs index 2007b624f..a9462dd32 100644 --- a/src/canvas/widgets/battery_display.rs +++ b/src/canvas/widgets/battery_display.rs @@ -13,7 +13,7 @@ use crate::{ Painter, }, constants::*, - data_conversion::BatteryDuration, + data_collection::batteries::BatteryState, }; impl Painter { @@ -27,8 +27,8 @@ impl Painter { .widget_states .get_mut(&widget_id) { - let is_on_widget = widget_id == app_state.current_widget.widget_id; - let border_style = if is_on_widget { + let is_selected = widget_id == app_state.current_widget.widget_id; + let border_style = if is_selected { self.styles.highlighted_border_style } else { self.styles.border_style @@ -42,7 +42,7 @@ impl Painter { let block = { let mut block = widget_block( app_state.app_config_fields.use_basic_mode, - is_on_widget, + is_selected, self.styles.border_type, ) .border_style(border_style) @@ -113,7 +113,7 @@ impl Painter { let margined_draw_loc = Layout::default() .constraints([Constraint::Percentage(100)]) - .horizontal_margin(u16::from(!(is_on_widget || is_basic))) + .horizontal_margin(u16::from(is_basic && !is_selected)) .direction(Direction::Horizontal) .split(draw_loc)[0]; @@ -124,13 +124,14 @@ impl Painter { { let full_width = draw_loc.width.saturating_sub(2); let bar_length = usize::from(full_width.saturating_sub(6)); - let charge_percentage = battery_details.charge_percentage; - let num_bars = calculate_basic_use_bars(charge_percentage, bar_length); + let charge_percent = battery_details.charge_percent; + + let num_bars = calculate_basic_use_bars(charge_percent, bar_length); let bars = format!( "[{}{}{:3.0}%]", "|".repeat(num_bars), " ".repeat(bar_length - num_bars), - charge_percentage, + charge_percent, ); let mut battery_charge_rows = Vec::with_capacity(2); @@ -138,9 +139,9 @@ impl Painter { Cell::from("Charge").style(self.styles.text_style) ])); battery_charge_rows.push(Row::new([Cell::from(bars).style( - if charge_percentage < 10.0 { + if charge_percent < 10.0 { self.styles.low_battery - } else if charge_percentage < 50.0 { + } else if charge_percent < 50.0 { self.styles.medium_battery } else { self.styles.high_battery @@ -148,49 +149,51 @@ impl Painter { )])); let mut battery_rows = Vec::with_capacity(3); + let watt_consumption = battery_details.watt_consumption(); + let health = battery_details.health(); + battery_rows.push(Row::new([""]).bottom_margin(table_gap + 1)); - battery_rows.push( - Row::new(["Rate", &battery_details.watt_consumption]) - .style(self.styles.text_style), - ); + battery_rows + .push(Row::new(["Rate", &watt_consumption]).style(self.styles.text_style)); battery_rows.push( - Row::new(["State", &battery_details.state]).style(self.styles.text_style), + Row::new(["State", battery_details.state.as_str()]) + .style(self.styles.text_style), ); let mut time: String; // Keep string lifetime in scope. { let style = self.styles.text_style; - match &battery_details.battery_duration { - BatteryDuration::ToEmpty(secs) => { + match &battery_details.state { + BatteryState::Charging { + time_to_full: Some(secs), + } => { time = long_time(*secs); if full_width as usize > time.len() { - battery_rows.push(Row::new(["Time to empty", &time]).style(style)); + battery_rows.push(Row::new(["Time to full", &time]).style(style)); } else { time = short_time(*secs); - battery_rows.push(Row::new(["To empty", &time]).style(style)); + battery_rows.push(Row::new(["To full", &time]).style(style)); } } - BatteryDuration::ToFull(secs) => { + BatteryState::Discharging { + time_to_empty: Some(secs), + } => { time = long_time(*secs); if full_width as usize > time.len() { - battery_rows.push(Row::new(["Time to full", &time]).style(style)); + battery_rows.push(Row::new(["Time to empty", &time]).style(style)); } else { time = short_time(*secs); - battery_rows.push(Row::new(["To full", &time]).style(style)); + battery_rows.push(Row::new(["To empty", &time]).style(style)); } } - BatteryDuration::Empty - | BatteryDuration::Full - | BatteryDuration::Unknown => {} + _ => {} } } - battery_rows.push( - Row::new(["Health", &battery_details.health]).style(self.styles.text_style), - ); + battery_rows.push(Row::new(["Health", &health]).style(self.styles.text_style)); let header = if app_state.converted_data.battery_data.len() > 1 { Row::new([""]).bottom_margin(table_gap) @@ -241,7 +244,7 @@ impl Painter { } } -fn get_hms(secs: i64) -> (i64, i64, i64) { +fn get_hms(secs: u32) -> (u32, u32, u32) { let hours = secs / (60 * 60); let minutes = (secs / 60) - hours * 60; let seconds = secs - minutes * 60 - hours * 60 * 60; @@ -249,7 +252,7 @@ fn get_hms(secs: i64) -> (i64, i64, i64) { (hours, minutes, seconds) } -fn long_time(secs: i64) -> String { +fn long_time(secs: u32) -> String { let (hours, minutes, seconds) = get_hms(secs); if hours > 0 { @@ -266,7 +269,7 @@ fn long_time(secs: i64) -> String { } } -fn short_time(secs: i64) -> String { +fn short_time(secs: u32) -> String { let (hours, minutes, seconds) = get_hms(secs); if hours > 0 { diff --git a/src/data_collection.rs b/src/data_collection.rs index 72ba7e4bd..37f33494a 100644 --- a/src/data_collection.rs +++ b/src/data_collection.rs @@ -6,9 +6,7 @@ pub mod nvidia; #[cfg(all(target_os = "linux", feature = "gpu"))] pub mod amd; -#[cfg(feature = "battery")] pub mod batteries; - pub mod cpu; pub mod disks; pub mod error; @@ -45,7 +43,7 @@ pub struct Data { pub disks: Option>, pub io: Option, #[cfg(feature = "battery")] - pub list_of_batteries: Option>, + pub list_of_batteries: Option>, #[cfg(feature = "zfs")] pub arc: Option, #[cfg(feature = "gpu")] diff --git a/src/data_collection/batteries.rs b/src/data_collection/batteries.rs index a155ad2d2..68b4ad5cd 100644 --- a/src/data_collection/batteries.rs +++ b/src/data_collection/batteries.rs @@ -1,11 +1,104 @@ -//! Data collection for batteries. +//! Uses the battery crate. //! -//! For Linux, macOS, Windows, FreeBSD, Dragonfly, and iOS, this is handled by -//! the battery crate. +//! Covers battery usage for: +//! - Linux 2.6.39+ +//! - MacOS 10.10+ +//! - iOS +//! - Windows 7+ +//! - FreeBSD +//! - DragonFlyBSD +//! +//! For more information, refer to the [starship_battery](https://github.com/starship/rust-battery) repo/docs. + +#[cfg(feature = "battery")] +use starship_battery::{ + units::{power::watt, ratio::percent, time::second}, + Battery, Manager, State, +}; + +/// Battery state. +#[derive(Debug, Default, Clone)] +pub enum BatteryState { + Charging { + /// Time to full in seconds. + time_to_full: Option, + }, + Discharging { + /// Time to empty in seconds. + time_to_empty: Option, + }, + Empty, + Full, + #[default] + Unknown, +} -cfg_if::cfg_if! { - if #[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "ios"))] { - pub mod battery; - pub use self::battery::*; +impl BatteryState { + /// Return the string representation. + pub fn as_str(&self) -> &'static str { + match self { + BatteryState::Charging { .. } => "Charging", + BatteryState::Discharging { .. } => "Discharging", + BatteryState::Empty => "Empty", + BatteryState::Full => "Full", + BatteryState::Unknown => "Unknown", + } } } + +#[derive(Debug, Clone)] +pub struct BatteryData { + /// Current charge percent. + pub charge_percent: f64, + /// Power consumption, in watts. + pub power_consumption: f64, + /// Reported battery health. + pub health_percent: f64, + /// The current battery "state" (e.g. is it full, charging, etc.). + pub state: BatteryState, +} + +impl BatteryData { + pub fn watt_consumption(&self) -> String { + format!("{:.2}W", self.power_consumption) + } + + pub fn health(&self) -> String { + format!("{:.2}%", self.health_percent) + } +} + +#[cfg(feature = "battery")] +pub fn refresh_batteries(manager: &Manager, batteries: &mut [Battery]) -> Vec { + batteries + .iter_mut() + .filter_map(|battery| { + if manager.refresh(battery).is_ok() { + Some(BatteryData { + charge_percent: f64::from(battery.state_of_charge().get::()), + power_consumption: f64::from(battery.energy_rate().get::()), + health_percent: f64::from(battery.state_of_health().get::()), + state: match battery.state() { + State::Unknown => BatteryState::Unknown, + State::Charging => BatteryState::Charging { + time_to_full: { + let optional_time = battery.time_to_full(); + optional_time.map(|time| f64::from(time.get::()) as u32) + }, + }, + State::Discharging => BatteryState::Discharging { + time_to_empty: { + let optional_time = battery.time_to_empty(); + optional_time.map(|time| f64::from(time.get::()) as u32) + }, + }, + State::Empty => BatteryState::Empty, + State::Full => BatteryState::Full, + }, + }) + } else { + None + } + }) + .collect::>() +} diff --git a/src/data_collection/batteries/battery.rs b/src/data_collection/batteries/battery.rs deleted file mode 100644 index ec95ada2c..000000000 --- a/src/data_collection/batteries/battery.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! Uses the battery crate from svartalf. -//! Covers battery usage for: -//! - Linux 2.6.39+ -//! - MacOS 10.10+ -//! - iOS -//! - Windows 7+ -//! - FreeBSD -//! - DragonFlyBSD -//! -//! For more information, refer to the [starship_battery](https://github.com/starship/rust-battery) repo/docs. - -use starship_battery::{ - units::{power::watt, ratio::percent, time::second}, - Battery, Manager, State, -}; - -#[derive(Debug, Clone)] -pub struct BatteryHarvest { - pub charge_percent: f64, - pub secs_until_full: Option, - pub secs_until_empty: Option, - pub power_consumption_rate_watts: f64, - pub health_percent: f64, - pub state: State, -} - -pub fn refresh_batteries(manager: &Manager, batteries: &mut [Battery]) -> Vec { - batteries - .iter_mut() - .filter_map(|battery| { - if manager.refresh(battery).is_ok() { - Some(BatteryHarvest { - secs_until_full: { - let optional_time = battery.time_to_full(); - optional_time.map(|time| f64::from(time.get::()) as i64) - }, - secs_until_empty: { - let optional_time = battery.time_to_empty(); - optional_time.map(|time| f64::from(time.get::()) as i64) - }, - charge_percent: f64::from(battery.state_of_charge().get::()), - power_consumption_rate_watts: f64::from(battery.energy_rate().get::()), - health_percent: f64::from(battery.state_of_health().get::()), - state: battery.state(), - }) - } else { - None - } - }) - .collect::>() -} diff --git a/src/data_conversion.rs b/src/data_conversion.rs index 2d7692202..a5d50ccba 100644 --- a/src/data_conversion.rs +++ b/src/data_conversion.rs @@ -8,30 +8,13 @@ use std::borrow::Cow; use crate::{ app::{data_farmer::DataCollection, AxisScaling}, canvas::components::time_chart::Point, - data_collection::{cpu::CpuDataType, memory::MemHarvest, temperature::TemperatureType}, + data_collection::{ + batteries::BatteryData, cpu::CpuDataType, memory::MemHarvest, temperature::TemperatureType, + }, utils::{data_prefixes::*, data_units::DataUnit}, widgets::{DiskWidgetData, TempWidgetData}, }; -#[derive(Debug, Default)] -pub enum BatteryDuration { - ToEmpty(i64), - ToFull(i64), - Empty, - Full, - #[default] - Unknown, -} - -#[derive(Default, Debug)] -pub struct ConvertedBatteryData { - pub charge_percentage: f64, - pub watt_consumption: String, - pub battery_duration: BatteryDuration, - pub health: String, - pub state: String, -} - #[derive(Default, Debug)] pub struct ConvertedNetworkData { pub rx: Vec, @@ -90,7 +73,8 @@ pub struct ConvertedData { pub load_avg_data: [f32; 3], pub cpu_data: Vec, - pub battery_data: Vec, + + pub battery_data: Vec, pub disk_data: Vec, pub temp_data: Vec, } @@ -508,37 +492,6 @@ pub fn dec_bytes_string(value: u64) -> String { } } -#[cfg(feature = "battery")] -pub fn convert_battery_harvest(current_data: &DataCollection) -> Vec { - current_data - .battery_harvest - .iter() - .map(|battery_harvest| ConvertedBatteryData { - charge_percentage: battery_harvest.charge_percent, - watt_consumption: format!("{:.2}W", battery_harvest.power_consumption_rate_watts), - battery_duration: if let Some(secs) = battery_harvest.secs_until_empty { - BatteryDuration::ToEmpty(secs) - } else if let Some(secs) = battery_harvest.secs_until_full { - BatteryDuration::ToFull(secs) - } else { - match battery_harvest.state { - starship_battery::State::Empty => BatteryDuration::Empty, - starship_battery::State::Full => BatteryDuration::Full, - _ => BatteryDuration::Unknown, - } - }, - health: format!("{:.2}%", battery_harvest.health_percent), - state: { - let mut s = battery_harvest.state.to_string(); - if !s.is_empty() { - s[0..1].make_ascii_uppercase(); - } - s - }, - }) - .collect() -} - #[cfg(feature = "zfs")] pub fn convert_arc_data_points(current_data: &DataCollection) -> Vec { let mut result: Vec = Vec::new(); diff --git a/src/lib.rs b/src/lib.rs index 25d05c75f..ad565d500 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -513,7 +513,7 @@ pub fn start_bottom() -> anyhow::Result<()> { { if app.used_widgets.use_battery { app.converted_data.battery_data = - convert_battery_harvest(&app.data_collection); + app.data_collection.battery_harvest.clone(); } } diff --git a/src/options.rs b/src/options.rs index 15c710268..1e025dd96 100644 --- a/src/options.rs +++ b/src/options.rs @@ -493,7 +493,7 @@ pub(crate) fn init_app(args: BottomArgs, config: Config) -> Result<(App, BottomL proc_state: ProcState::init(proc_state_map), temp_state: TempState::init(temp_state_map), disk_state: DiskState::init(disk_state_map), - battery_state: BatteryState::init(battery_state_map), + battery_state: AppBatteryState::init(battery_state_map), basic_table_widget_state, };