mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-27 07:34:27 +02:00
refactor: rename files from mod to their directory names
This commit is contained in:
parent
b5e6dea324
commit
189be96622
364
src/app/data_harvester.rs
Normal file
364
src/app/data_harvester.rs
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
//! This is the main file to house data collection functions.
|
||||||
|
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
use fxhash::FxHashMap;
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
use sysinfo::{System, SystemExt};
|
||||||
|
|
||||||
|
use battery::{Battery, Manager};
|
||||||
|
|
||||||
|
use futures::join;
|
||||||
|
|
||||||
|
use super::{DataFilters, UsedWidgets};
|
||||||
|
|
||||||
|
pub mod batteries;
|
||||||
|
pub mod cpu;
|
||||||
|
pub mod disks;
|
||||||
|
pub mod memory;
|
||||||
|
pub mod network;
|
||||||
|
pub mod processes;
|
||||||
|
pub mod temperature;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Data {
|
||||||
|
pub last_collection_time: Instant,
|
||||||
|
pub cpu: Option<cpu::CpuHarvest>,
|
||||||
|
pub load_avg: Option<cpu::LoadAvgHarvest>,
|
||||||
|
pub memory: Option<memory::MemHarvest>,
|
||||||
|
pub swap: Option<memory::MemHarvest>,
|
||||||
|
pub temperature_sensors: Option<Vec<temperature::TempHarvest>>,
|
||||||
|
pub network: Option<network::NetworkHarvest>,
|
||||||
|
pub list_of_processes: Option<Vec<processes::ProcessHarvest>>,
|
||||||
|
pub disks: Option<Vec<disks::DiskHarvest>>,
|
||||||
|
pub io: Option<disks::IoHarvest>,
|
||||||
|
pub list_of_batteries: Option<Vec<batteries::BatteryHarvest>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Data {
|
||||||
|
fn default() -> Self {
|
||||||
|
Data {
|
||||||
|
last_collection_time: Instant::now(),
|
||||||
|
cpu: None,
|
||||||
|
load_avg: None,
|
||||||
|
memory: None,
|
||||||
|
swap: None,
|
||||||
|
temperature_sensors: None,
|
||||||
|
list_of_processes: None,
|
||||||
|
disks: None,
|
||||||
|
io: None,
|
||||||
|
network: None,
|
||||||
|
list_of_batteries: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Data {
|
||||||
|
pub fn cleanup(&mut self) {
|
||||||
|
self.io = None;
|
||||||
|
self.temperature_sensors = None;
|
||||||
|
self.list_of_processes = None;
|
||||||
|
self.disks = None;
|
||||||
|
self.memory = None;
|
||||||
|
self.swap = None;
|
||||||
|
self.cpu = None;
|
||||||
|
self.load_avg = None;
|
||||||
|
|
||||||
|
if let Some(network) = &mut self.network {
|
||||||
|
network.first_run_cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DataCollector {
|
||||||
|
pub data: Data,
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
sys: System,
|
||||||
|
previous_cpu_times: Vec<(cpu::PastCpuWork, cpu::PastCpuTotal)>,
|
||||||
|
previous_average_cpu_time: Option<(cpu::PastCpuWork, cpu::PastCpuTotal)>,
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pid_mapping: FxHashMap<crate::Pid, processes::PrevProcDetails>,
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
prev_idle: f64,
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
prev_non_idle: f64,
|
||||||
|
mem_total_kb: u64,
|
||||||
|
temperature_type: temperature::TemperatureType,
|
||||||
|
use_current_cpu_total: bool,
|
||||||
|
last_collection_time: Instant,
|
||||||
|
total_rx: u64,
|
||||||
|
total_tx: u64,
|
||||||
|
show_average_cpu: bool,
|
||||||
|
widgets_to_harvest: UsedWidgets,
|
||||||
|
battery_manager: Option<Manager>,
|
||||||
|
battery_list: Option<Vec<Battery>>,
|
||||||
|
filters: DataFilters,
|
||||||
|
}
|
||||||
|
|
||||||
|
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()),
|
||||||
|
previous_cpu_times: vec![],
|
||||||
|
previous_average_cpu_time: None,
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pid_mapping: FxHashMap::default(),
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
prev_idle: 0_f64,
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
prev_non_idle: 0_f64,
|
||||||
|
mem_total_kb: 0,
|
||||||
|
temperature_type: temperature::TemperatureType::Celsius,
|
||||||
|
use_current_cpu_total: false,
|
||||||
|
last_collection_time: Instant::now(),
|
||||||
|
total_rx: 0,
|
||||||
|
total_tx: 0,
|
||||||
|
show_average_cpu: false,
|
||||||
|
widgets_to_harvest: UsedWidgets::default(),
|
||||||
|
battery_manager: None,
|
||||||
|
battery_list: None,
|
||||||
|
filters,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(&mut self) {
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
futures::executor::block_on(self.initialize_memory_size());
|
||||||
|
}
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh network list once...
|
||||||
|
if cfg!(target_os = "windows") && self.widgets_to_harvest.use_net {
|
||||||
|
self.sys.refresh_networks_list();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.widgets_to_harvest.use_battery {
|
||||||
|
if let Ok(battery_manager) = Manager::new() {
|
||||||
|
if let Ok(batteries) = battery_manager.batteries() {
|
||||||
|
let battery_list: Vec<Battery> = batteries.filter_map(Result::ok).collect();
|
||||||
|
if !battery_list.is_empty() {
|
||||||
|
self.battery_list = Some(battery_list);
|
||||||
|
self.battery_manager = Some(battery_manager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
futures::executor::block_on(self.update_data());
|
||||||
|
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(250));
|
||||||
|
|
||||||
|
self.data.cleanup();
|
||||||
|
|
||||||
|
// trace!("Enabled widgets to harvest: {:#?}", self.widgets_to_harvest);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
async fn initialize_memory_size(&mut self) {
|
||||||
|
self.mem_total_kb = if let Ok(mem) = heim::memory::memory().await {
|
||||||
|
mem.total().get::<heim::units::information::kilobyte>()
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_collected_data(&mut self, used_widgets: UsedWidgets) {
|
||||||
|
self.widgets_to_harvest = used_widgets;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_temperature_type(&mut self, temperature_type: temperature::TemperatureType) {
|
||||||
|
self.temperature_type = temperature_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_use_current_cpu_total(&mut self, use_current_cpu_total: bool) {
|
||||||
|
self.use_current_cpu_total = use_current_cpu_total;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_show_average_cpu(&mut self, show_average_cpu: bool) {
|
||||||
|
self.show_average_cpu = show_average_cpu;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_data(&mut self) {
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
{
|
||||||
|
if self.widgets_to_harvest.use_proc {
|
||||||
|
self.sys.refresh_processes();
|
||||||
|
}
|
||||||
|
if self.widgets_to_harvest.use_temp {
|
||||||
|
self.sys.refresh_components();
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg!(target_os = "windows") && self.widgets_to_harvest.use_net {
|
||||||
|
self.sys.refresh_networks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let current_instant = std::time::Instant::now();
|
||||||
|
|
||||||
|
// CPU
|
||||||
|
if self.widgets_to_harvest.use_cpu {
|
||||||
|
if let Ok(cpu_data) = cpu::get_cpu_data_list(
|
||||||
|
self.show_average_cpu,
|
||||||
|
&mut self.previous_cpu_times,
|
||||||
|
&mut self.previous_average_cpu_time,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
self.data.cpu = Some(cpu_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
{
|
||||||
|
// Load Average
|
||||||
|
if let Ok(load_avg_data) = cpu::get_load_avg().await {
|
||||||
|
self.data.load_avg = Some(load_avg_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batteries
|
||||||
|
if let Some(battery_manager) = &self.battery_manager {
|
||||||
|
if let Some(battery_list) = &mut self.battery_list {
|
||||||
|
self.data.list_of_batteries =
|
||||||
|
Some(batteries::refresh_batteries(battery_manager, battery_list));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.widgets_to_harvest.use_proc {
|
||||||
|
if let Ok(process_list) = {
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
processes::get_process_data(
|
||||||
|
&mut self.prev_idle,
|
||||||
|
&mut self.prev_non_idle,
|
||||||
|
&mut self.pid_mapping,
|
||||||
|
self.use_current_cpu_total,
|
||||||
|
current_instant
|
||||||
|
.duration_since(self.last_collection_time)
|
||||||
|
.as_secs(),
|
||||||
|
self.mem_total_kb,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
{
|
||||||
|
processes::get_process_data(
|
||||||
|
&self.sys,
|
||||||
|
self.use_current_cpu_total,
|
||||||
|
self.mem_total_kb,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} {
|
||||||
|
self.data.list_of_processes = Some(process_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let network_data_fut = {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
network::get_network_data(
|
||||||
|
&self.sys,
|
||||||
|
self.last_collection_time,
|
||||||
|
&mut self.total_rx,
|
||||||
|
&mut self.total_tx,
|
||||||
|
current_instant,
|
||||||
|
self.widgets_to_harvest.use_net,
|
||||||
|
&self.filters.net_filter,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
{
|
||||||
|
network::get_network_data(
|
||||||
|
self.last_collection_time,
|
||||||
|
&mut self.total_rx,
|
||||||
|
&mut self.total_tx,
|
||||||
|
current_instant,
|
||||||
|
self.widgets_to_harvest.use_net,
|
||||||
|
&self.filters.net_filter,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mem_data_fut = memory::get_mem_data(self.widgets_to_harvest.use_mem);
|
||||||
|
let disk_data_fut = disks::get_disk_usage(
|
||||||
|
self.widgets_to_harvest.use_disk,
|
||||||
|
&self.filters.disk_filter,
|
||||||
|
&self.filters.mount_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"))]
|
||||||
|
{
|
||||||
|
temperature::get_temperature_data(
|
||||||
|
&self.sys,
|
||||||
|
&self.temperature_type,
|
||||||
|
self.widgets_to_harvest.use_temp,
|
||||||
|
&self.filters.temp_filter,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
temperature::get_temperature_data(
|
||||||
|
&self.temperature_type,
|
||||||
|
self.widgets_to_harvest.use_temp,
|
||||||
|
&self.filters.temp_filter,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let (net_data, mem_res, disk_res, io_res, temp_res) = join!(
|
||||||
|
network_data_fut,
|
||||||
|
mem_data_fut,
|
||||||
|
disk_data_fut,
|
||||||
|
disk_io_usage_fut,
|
||||||
|
temp_data_fut
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Ok(net_data) = net_data {
|
||||||
|
if let Some(net_data) = &net_data {
|
||||||
|
self.total_rx = net_data.total_rx;
|
||||||
|
self.total_tx = net_data.total_tx;
|
||||||
|
}
|
||||||
|
self.data.network = net_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(memory) = mem_res.0 {
|
||||||
|
self.data.memory = memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(swap) = mem_res.1 {
|
||||||
|
self.data.swap = swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(disks) = disk_res {
|
||||||
|
self.data.disks = disks;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(io) = io_res {
|
||||||
|
self.data.io = io;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(temp) = temp_res {
|
||||||
|
self.data.temperature_sensors = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update time
|
||||||
|
self.data.last_collection_time = current_instant;
|
||||||
|
self.last_collection_time = current_instant;
|
||||||
|
}
|
||||||
|
}
|
10
src/app/data_harvester/batteries.rs
Normal file
10
src/app/data_harvester/batteries.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
//! Data collection for batteries.
|
||||||
|
//!
|
||||||
|
//! For Linux, macOS, Windows, FreeBSD, Dragonfly, and iOS, this is handled by the battery crate.
|
||||||
|
|
||||||
|
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::*;
|
||||||
|
}
|
||||||
|
}
|
14
src/app/data_harvester/cpu.rs
Normal file
14
src/app/data_harvester/cpu.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
//! Data collection for CPU usage and load average.
|
||||||
|
//!
|
||||||
|
//! For CPU usage, Linux, macOS, and Windows are handled by Heim.
|
||||||
|
//!
|
||||||
|
//! For load average, macOS and Linux are supported through Heim.
|
||||||
|
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] {
|
||||||
|
pub mod heim;
|
||||||
|
pub use self::heim::*;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type LoadAvgHarvest = [f32; 3];
|
170
src/app/data_harvester/cpu/heim.rs
Normal file
170
src/app/data_harvester/cpu/heim.rs
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
//! CPU stats through heim.
|
||||||
|
//! Supports macOS, Linux, and Windows.
|
||||||
|
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(target_os = "linux")] {
|
||||||
|
pub mod linux;
|
||||||
|
pub use linux::*;
|
||||||
|
} else if #[cfg(any(target_os = "macos", target_os = "windows"))] {
|
||||||
|
pub mod windows_macos;
|
||||||
|
pub use windows_macos::*;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(target_family = "unix")] {
|
||||||
|
pub mod unix;
|
||||||
|
pub use unix::*;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone)]
|
||||||
|
pub struct CpuData {
|
||||||
|
pub cpu_prefix: String,
|
||||||
|
pub cpu_count: Option<usize>,
|
||||||
|
pub cpu_usage: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type CpuHarvest = Vec<CpuData>;
|
||||||
|
|
||||||
|
pub type PastCpuWork = f64;
|
||||||
|
pub type PastCpuTotal = f64;
|
||||||
|
|
||||||
|
use futures::StreamExt;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
pub async fn get_cpu_data_list(
|
||||||
|
show_average_cpu: bool, previous_cpu_times: &mut Vec<(PastCpuWork, PastCpuTotal)>,
|
||||||
|
previous_average_cpu_time: &mut Option<(PastCpuWork, PastCpuTotal)>,
|
||||||
|
) -> crate::error::Result<CpuHarvest> {
|
||||||
|
fn calculate_cpu_usage_percentage(
|
||||||
|
(previous_working_time, previous_total_time): (f64, f64),
|
||||||
|
(current_working_time, current_total_time): (f64, f64),
|
||||||
|
) -> f64 {
|
||||||
|
((if current_working_time > previous_working_time {
|
||||||
|
current_working_time - previous_working_time
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}) * 100.0)
|
||||||
|
/ (if current_total_time > previous_total_time {
|
||||||
|
current_total_time - previous_total_time
|
||||||
|
} else {
|
||||||
|
1.0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all CPU times...
|
||||||
|
let cpu_times = heim::cpu::times().await?;
|
||||||
|
futures::pin_mut!(cpu_times);
|
||||||
|
|
||||||
|
let mut cpu_deque: VecDeque<CpuData> = if previous_cpu_times.is_empty() {
|
||||||
|
// Must initialize ourselves. Use a very quick timeout to calculate an initial.
|
||||||
|
futures_timer::Delay::new(std::time::Duration::from_millis(100)).await;
|
||||||
|
|
||||||
|
let second_cpu_times = heim::cpu::times().await?;
|
||||||
|
futures::pin_mut!(second_cpu_times);
|
||||||
|
|
||||||
|
let mut new_cpu_times: Vec<(PastCpuWork, PastCpuTotal)> = Vec::new();
|
||||||
|
let mut cpu_deque: VecDeque<CpuData> = VecDeque::new();
|
||||||
|
let mut collected_zip = cpu_times.zip(second_cpu_times).enumerate(); // Gotta move it here, can't on while line.
|
||||||
|
|
||||||
|
while let Some((itx, (past, present))) = collected_zip.next().await {
|
||||||
|
if let (Ok(past), Ok(present)) = (past, present) {
|
||||||
|
let present_times = convert_cpu_times(&present);
|
||||||
|
new_cpu_times.push(present_times);
|
||||||
|
cpu_deque.push_back(CpuData {
|
||||||
|
cpu_prefix: "CPU".to_string(),
|
||||||
|
cpu_count: Some(itx),
|
||||||
|
cpu_usage: calculate_cpu_usage_percentage(
|
||||||
|
convert_cpu_times(&past),
|
||||||
|
present_times,
|
||||||
|
),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
new_cpu_times.push((0.0, 0.0));
|
||||||
|
cpu_deque.push_back(CpuData {
|
||||||
|
cpu_prefix: "CPU".to_string(),
|
||||||
|
cpu_count: Some(itx),
|
||||||
|
cpu_usage: 0.0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*previous_cpu_times = new_cpu_times;
|
||||||
|
cpu_deque
|
||||||
|
} else {
|
||||||
|
let (new_cpu_times, cpu_deque): (Vec<(PastCpuWork, PastCpuTotal)>, VecDeque<CpuData>) =
|
||||||
|
cpu_times
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.await
|
||||||
|
.iter()
|
||||||
|
.zip(&*previous_cpu_times)
|
||||||
|
.enumerate()
|
||||||
|
.map(|(itx, (current_cpu, (past_cpu_work, past_cpu_total)))| {
|
||||||
|
if let Ok(cpu_time) = current_cpu {
|
||||||
|
let present_times = convert_cpu_times(cpu_time);
|
||||||
|
|
||||||
|
(
|
||||||
|
present_times,
|
||||||
|
CpuData {
|
||||||
|
cpu_prefix: "CPU".to_string(),
|
||||||
|
cpu_count: Some(itx),
|
||||||
|
cpu_usage: calculate_cpu_usage_percentage(
|
||||||
|
(*past_cpu_work, *past_cpu_total),
|
||||||
|
present_times,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
(*past_cpu_work, *past_cpu_total),
|
||||||
|
CpuData {
|
||||||
|
cpu_prefix: "CPU".to_string(),
|
||||||
|
cpu_count: Some(itx),
|
||||||
|
cpu_usage: 0.0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unzip();
|
||||||
|
|
||||||
|
*previous_cpu_times = new_cpu_times;
|
||||||
|
cpu_deque
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get average CPU if needed... and slap it at the top
|
||||||
|
if show_average_cpu {
|
||||||
|
let cpu_time = heim::cpu::time().await?;
|
||||||
|
|
||||||
|
let (cpu_usage, new_average_cpu_time) = if let Some((past_cpu_work, past_cpu_total)) =
|
||||||
|
previous_average_cpu_time
|
||||||
|
{
|
||||||
|
let present_times = convert_cpu_times(&cpu_time);
|
||||||
|
(
|
||||||
|
calculate_cpu_usage_percentage((*past_cpu_work, *past_cpu_total), present_times),
|
||||||
|
present_times,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Again, we need to do a quick timeout...
|
||||||
|
futures_timer::Delay::new(std::time::Duration::from_millis(100)).await;
|
||||||
|
let second_cpu_time = heim::cpu::time().await?;
|
||||||
|
|
||||||
|
let present_times = convert_cpu_times(&second_cpu_time);
|
||||||
|
(
|
||||||
|
calculate_cpu_usage_percentage(convert_cpu_times(&cpu_time), present_times),
|
||||||
|
present_times,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
*previous_average_cpu_time = Some(new_average_cpu_time);
|
||||||
|
cpu_deque.push_front(CpuData {
|
||||||
|
cpu_prefix: "AVG".to_string(),
|
||||||
|
cpu_count: None,
|
||||||
|
cpu_usage,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ok(Vec::from(cpu_deque.drain(0..3).collect::<Vec<_>>())) // For artificially limiting the CPU results
|
||||||
|
|
||||||
|
Ok(Vec::from(cpu_deque))
|
||||||
|
}
|
10
src/app/data_harvester/disks.rs
Normal file
10
src/app/data_harvester/disks.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
//! Data collection for disks (IO, usage, space, etc.).
|
||||||
|
//!
|
||||||
|
//! For Linux, macOS, and Windows, this is handled by heim.
|
||||||
|
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] {
|
||||||
|
pub mod heim;
|
||||||
|
pub use self::heim::*;
|
||||||
|
}
|
||||||
|
}
|
154
src/app/data_harvester/disks/heim.rs
Normal file
154
src/app/data_harvester/disks/heim.rs
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
use crate::app::Filter;
|
||||||
|
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(target_os = "linux")] {
|
||||||
|
pub mod linux;
|
||||||
|
pub use linux::*;
|
||||||
|
} else if #[cfg(any(target_os = "macos", target_os = "windows"))] {
|
||||||
|
pub mod windows_macos;
|
||||||
|
pub use windows_macos::*;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct DiskHarvest {
|
||||||
|
pub name: String,
|
||||||
|
pub mount_point: String,
|
||||||
|
pub free_space: Option<u64>,
|
||||||
|
pub used_space: Option<u64>,
|
||||||
|
pub total_space: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct IoData {
|
||||||
|
pub read_bytes: u64,
|
||||||
|
pub write_bytes: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type IoHarvest = std::collections::HashMap<String, Option<IoData>>;
|
||||||
|
|
||||||
|
pub async fn get_io_usage(actually_get: bool) -> crate::utils::error::Result<Option<IoHarvest>> {
|
||||||
|
if !actually_get {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
use futures::StreamExt;
|
||||||
|
|
||||||
|
let mut io_hash: std::collections::HashMap<String, Option<IoData>> =
|
||||||
|
std::collections::HashMap::new();
|
||||||
|
|
||||||
|
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::<heim::units::information::byte>(),
|
||||||
|
write_bytes: io.write_bytes().get::<heim::units::information::byte>(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Some(io_hash))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_disk_usage(
|
||||||
|
actually_get: bool, disk_filter: &Option<Filter>, mount_filter: &Option<Filter>,
|
||||||
|
) -> crate::utils::error::Result<Option<Vec<DiskHarvest>>> {
|
||||||
|
if !actually_get {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
use futures::StreamExt;
|
||||||
|
|
||||||
|
let mut vec_disks: Vec<DiskHarvest> = Vec::new();
|
||||||
|
let partitions_stream = heim::disk::partitions_physical().await?;
|
||||||
|
futures::pin_mut!(partitions_stream);
|
||||||
|
|
||||||
|
while let Some(part) = partitions_stream.next().await {
|
||||||
|
if let Ok(partition) = part {
|
||||||
|
let name = get_device_name(&partition);
|
||||||
|
|
||||||
|
let mount_point = (partition
|
||||||
|
.mount_point()
|
||||||
|
.to_str()
|
||||||
|
.unwrap_or("Name Unavailable"))
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
// Precedence ordering in the case where name and mount filters disagree, "allow" takes precedence over "deny".
|
||||||
|
//
|
||||||
|
// For implementation, we do this as follows:
|
||||||
|
// 1. Is the entry allowed through any filter? That is, does it match an entry in a filter where `is_list_ignored` is `false`? If so, we always keep this entry.
|
||||||
|
// 2. Is the entry denied through any filter? That is, does it match an entry in a filter where `is_list_ignored` is `true`? If so, we always deny this entry.
|
||||||
|
// 3. Anything else is allowed.
|
||||||
|
|
||||||
|
let filter_check_map = [(disk_filter, &name), (mount_filter, &mount_point)];
|
||||||
|
|
||||||
|
// This represents case 1. That is, if there is a match in an allowing list - if there is, then
|
||||||
|
// immediately allow it!
|
||||||
|
let matches_allow_list = filter_check_map.iter().any(|(filter, text)| {
|
||||||
|
if let Some(filter) = filter {
|
||||||
|
if !filter.is_list_ignored {
|
||||||
|
for r in &filter.list {
|
||||||
|
if r.is_match(text) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
});
|
||||||
|
|
||||||
|
let to_keep = if matches_allow_list {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
// If it doesn't match an allow list, then check if it is denied.
|
||||||
|
// That is, if it matches in a reject filter, then reject. Otherwise, we always keep it.
|
||||||
|
!filter_check_map.iter().any(|(filter, text)| {
|
||||||
|
if let Some(filter) = filter {
|
||||||
|
if filter.is_list_ignored {
|
||||||
|
for r in &filter.list {
|
||||||
|
if r.is_match(text) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
if to_keep {
|
||||||
|
// The usage line can fail in some cases (for example, if you use Void Linux + LUKS,
|
||||||
|
// see https://github.com/ClementTsang/bottom/issues/419 for details). As such, check
|
||||||
|
// it like this instead.
|
||||||
|
if let Ok(usage) = heim::disk::usage(partition.mount_point().to_path_buf()).await {
|
||||||
|
vec_disks.push(DiskHarvest {
|
||||||
|
free_space: Some(usage.free().get::<heim::units::information::byte>()),
|
||||||
|
used_space: Some(usage.used().get::<heim::units::information::byte>()),
|
||||||
|
total_space: Some(usage.total().get::<heim::units::information::byte>()),
|
||||||
|
mount_point,
|
||||||
|
name,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
vec_disks.push(DiskHarvest {
|
||||||
|
free_space: None,
|
||||||
|
used_space: None,
|
||||||
|
total_space: None,
|
||||||
|
mount_point,
|
||||||
|
name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec_disks.sort_by(|a, b| a.name.cmp(&b.name));
|
||||||
|
|
||||||
|
Ok(Some(vec_disks))
|
||||||
|
}
|
10
src/app/data_harvester/memory.rs
Normal file
10
src/app/data_harvester/memory.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
//! Data collection for memory.
|
||||||
|
//!
|
||||||
|
//! For Linux, macOS, and Windows, this is handled by Heim.
|
||||||
|
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] {
|
||||||
|
pub mod general;
|
||||||
|
pub use self::general::*;
|
||||||
|
}
|
||||||
|
}
|
30
src/app/data_harvester/network.rs
Normal file
30
src/app/data_harvester/network.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
//! Data collection for network usage/IO.
|
||||||
|
//!
|
||||||
|
//! For Linux and macOS, this is handled by Heim.
|
||||||
|
//! For Windows, this is handled by sysinfo.
|
||||||
|
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(any(target_os = "linux", target_os = "macos"))] {
|
||||||
|
pub mod heim;
|
||||||
|
pub use self::heim::*;
|
||||||
|
} else if #[cfg(target_os = "windows")] {
|
||||||
|
pub mod sysinfo;
|
||||||
|
pub use self::sysinfo::*;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Debug)]
|
||||||
|
/// All units in bits.
|
||||||
|
pub struct NetworkHarvest {
|
||||||
|
pub rx: u64,
|
||||||
|
pub tx: u64,
|
||||||
|
pub total_rx: u64,
|
||||||
|
pub total_tx: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetworkHarvest {
|
||||||
|
pub fn first_run_cleanup(&mut self) {
|
||||||
|
self.rx = 0;
|
||||||
|
self.tx = 0;
|
||||||
|
}
|
||||||
|
}
|
97
src/app/data_harvester/processes.rs
Normal file
97
src/app/data_harvester/processes.rs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
//! Data collection for processes.
|
||||||
|
//!
|
||||||
|
//! For Linux, this is handled by a custom set of functions.
|
||||||
|
//! For Windows and macOS, this is handled by sysinfo.
|
||||||
|
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(target_os = "linux")] {
|
||||||
|
pub mod linux;
|
||||||
|
pub use self::linux::*;
|
||||||
|
} else if #[cfg(target_os = "macos")] {
|
||||||
|
pub mod macos;
|
||||||
|
pub use self::macos::*;
|
||||||
|
} else if #[cfg(target_os = "windows")] {
|
||||||
|
pub mod windows;
|
||||||
|
pub use self::windows::*;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(target_family = "unix")] {
|
||||||
|
pub mod unix;
|
||||||
|
pub use self::unix::*;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use crate::Pid;
|
||||||
|
|
||||||
|
// TODO: Add value so we know if it's sorted ascending or descending by default?
|
||||||
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
|
pub enum ProcessSorting {
|
||||||
|
CpuPercent,
|
||||||
|
Mem,
|
||||||
|
MemPercent,
|
||||||
|
Pid,
|
||||||
|
ProcessName,
|
||||||
|
Command,
|
||||||
|
ReadPerSecond,
|
||||||
|
WritePerSecond,
|
||||||
|
TotalRead,
|
||||||
|
TotalWrite,
|
||||||
|
State,
|
||||||
|
User,
|
||||||
|
Count,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for ProcessSorting {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
match &self {
|
||||||
|
ProcessSorting::CpuPercent => "CPU%",
|
||||||
|
ProcessSorting::MemPercent => "Mem%",
|
||||||
|
ProcessSorting::Mem => "Mem",
|
||||||
|
ProcessSorting::ReadPerSecond => "R/s",
|
||||||
|
ProcessSorting::WritePerSecond => "W/s",
|
||||||
|
ProcessSorting::TotalRead => "T.Read",
|
||||||
|
ProcessSorting::TotalWrite => "T.Write",
|
||||||
|
ProcessSorting::State => "State",
|
||||||
|
ProcessSorting::ProcessName => "Name",
|
||||||
|
ProcessSorting::Command => "Command",
|
||||||
|
ProcessSorting::Pid => "PID",
|
||||||
|
ProcessSorting::Count => "Count",
|
||||||
|
ProcessSorting::User => "User",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ProcessSorting {
|
||||||
|
fn default() -> Self {
|
||||||
|
ProcessSorting::CpuPercent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct ProcessHarvest {
|
||||||
|
pub pid: Pid,
|
||||||
|
pub parent_pid: Option<Pid>, // Remember, parent_pid 0 is root...
|
||||||
|
pub cpu_usage_percent: f64,
|
||||||
|
pub mem_usage_percent: f64,
|
||||||
|
pub mem_usage_bytes: u64,
|
||||||
|
// pub rss_kb: u64,
|
||||||
|
// pub virt_kb: u64,
|
||||||
|
pub name: String,
|
||||||
|
pub command: String,
|
||||||
|
pub read_bytes_per_sec: u64,
|
||||||
|
pub write_bytes_per_sec: u64,
|
||||||
|
pub total_read_bytes: u64,
|
||||||
|
pub total_write_bytes: u64,
|
||||||
|
pub process_state: String,
|
||||||
|
pub process_state_char: char,
|
||||||
|
|
||||||
|
/// This is the *effective* user ID.
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
pub uid: Option<libc::uid_t>,
|
||||||
|
}
|
73
src/app/data_harvester/temperature.rs
Normal file
73
src/app/data_harvester/temperature.rs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
//! Data collection for temperature metrics.
|
||||||
|
//!
|
||||||
|
//! For Linux and macOS, this is handled by Heim.
|
||||||
|
//! For Windows, this is handled by sysinfo.
|
||||||
|
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(target_os = "linux")] {
|
||||||
|
pub mod heim;
|
||||||
|
pub use self::heim::*;
|
||||||
|
} else if #[cfg(any(target_os = "macos", target_os = "windows"))] {
|
||||||
|
pub mod sysinfo;
|
||||||
|
pub use self::sysinfo::*;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
use crate::app::Filter;
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone)]
|
||||||
|
pub struct TempHarvest {
|
||||||
|
pub name: String,
|
||||||
|
pub temperature: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum TemperatureType {
|
||||||
|
Celsius,
|
||||||
|
Kelvin,
|
||||||
|
Fahrenheit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TemperatureType {
|
||||||
|
fn default() -> Self {
|
||||||
|
TemperatureType::Celsius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_temp_filtered(filter: &Option<Filter>, text: &str) -> bool {
|
||||||
|
if let Some(filter) = filter {
|
||||||
|
if filter.is_list_ignored {
|
||||||
|
let mut ret = true;
|
||||||
|
for r in &filter.list {
|
||||||
|
if r.is_match(text) {
|
||||||
|
ret = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn temp_vec_sort(temperature_vec: &mut Vec<TempHarvest>) {
|
||||||
|
// By default, sort temperature, then by alphabetically!
|
||||||
|
// TODO: [TEMPS] Allow users to control this.
|
||||||
|
|
||||||
|
// Note we sort in reverse here; we want greater temps to be higher priority.
|
||||||
|
temperature_vec.sort_by(|a, b| match a.temperature.partial_cmp(&b.temperature) {
|
||||||
|
Some(x) => match x {
|
||||||
|
Ordering::Less => Ordering::Greater,
|
||||||
|
Ordering::Greater => Ordering::Less,
|
||||||
|
Ordering::Equal => Ordering::Equal,
|
||||||
|
},
|
||||||
|
None => Ordering::Equal,
|
||||||
|
});
|
||||||
|
|
||||||
|
temperature_vec.sort_by(|a, b| a.name.partial_cmp(&b.name).unwrap_or(Ordering::Equal));
|
||||||
|
}
|
210
src/app/widgets.rs
Normal file
210
src/app/widgets.rs
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use crossterm::event::{KeyEvent, MouseEvent};
|
||||||
|
use enum_dispatch::enum_dispatch;
|
||||||
|
use tui::{layout::Rect, widgets::TableState};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
app::{
|
||||||
|
event::{EventResult, SelectionAction},
|
||||||
|
layout_manager::BottomWidgetType,
|
||||||
|
},
|
||||||
|
constants,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub mod base;
|
||||||
|
pub use base::*;
|
||||||
|
|
||||||
|
pub mod process;
|
||||||
|
pub use process::*;
|
||||||
|
|
||||||
|
pub mod net;
|
||||||
|
pub use net::*;
|
||||||
|
|
||||||
|
pub mod mem;
|
||||||
|
pub use mem::*;
|
||||||
|
|
||||||
|
pub mod cpu;
|
||||||
|
pub use cpu::*;
|
||||||
|
|
||||||
|
pub mod disk;
|
||||||
|
pub use disk::*;
|
||||||
|
|
||||||
|
pub mod battery;
|
||||||
|
pub use self::battery::*;
|
||||||
|
|
||||||
|
pub mod temp;
|
||||||
|
pub use temp::*;
|
||||||
|
|
||||||
|
/// A trait for things that are drawn with state.
|
||||||
|
#[enum_dispatch]
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
pub trait Component {
|
||||||
|
/// Handles a [`KeyEvent`].
|
||||||
|
///
|
||||||
|
/// Defaults to returning [`EventResult::NoRedraw`], indicating nothing should be done.
|
||||||
|
fn handle_key_event(&mut self, event: KeyEvent) -> EventResult {
|
||||||
|
EventResult::NoRedraw
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles a [`MouseEvent`].
|
||||||
|
///
|
||||||
|
/// Defaults to returning [`EventResult::Continue`], indicating nothing should be done.
|
||||||
|
fn handle_mouse_event(&mut self, event: MouseEvent) -> EventResult {
|
||||||
|
EventResult::NoRedraw
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a [`Component`]'s bounding box. Note that these are defined in *global*, *absolute*
|
||||||
|
/// coordinates.
|
||||||
|
fn bounds(&self) -> Rect;
|
||||||
|
|
||||||
|
/// Updates a [`Component`]s bounding box to `new_bounds`.
|
||||||
|
fn set_bounds(&mut self, new_bounds: Rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait for actual fully-fledged widgets to be displayed in bottom.
|
||||||
|
#[enum_dispatch]
|
||||||
|
pub trait Widget {
|
||||||
|
/// Updates a [`Widget`] given some data. Defaults to doing nothing.
|
||||||
|
fn update(&mut self) {}
|
||||||
|
|
||||||
|
/// Handles what to do when trying to respond to a widget selection movement to the left.
|
||||||
|
/// Defaults to just moving to the next-possible widget in that direction.
|
||||||
|
fn handle_widget_selection_left(&mut self) -> SelectionAction {
|
||||||
|
SelectionAction::NotHandled
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles what to do when trying to respond to a widget selection movement to the right.
|
||||||
|
/// Defaults to just moving to the next-possible widget in that direction.
|
||||||
|
fn handle_widget_selection_right(&mut self) -> SelectionAction {
|
||||||
|
SelectionAction::NotHandled
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles what to do when trying to respond to a widget selection movement upward.
|
||||||
|
/// Defaults to just moving to the next-possible widget in that direction.
|
||||||
|
fn handle_widget_selection_up(&mut self) -> SelectionAction {
|
||||||
|
SelectionAction::NotHandled
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles what to do when trying to respond to a widget selection movement downward.
|
||||||
|
/// Defaults to just moving to the next-possible widget in that direction.
|
||||||
|
fn handle_widget_selection_down(&mut self) -> SelectionAction {
|
||||||
|
SelectionAction::NotHandled
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_pretty_name(&self) -> &'static str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The "main" widgets that are used by bottom to display information!
|
||||||
|
#[enum_dispatch(Component, Widget)]
|
||||||
|
pub enum TmpBottomWidget {
|
||||||
|
MemGraph,
|
||||||
|
TempTable,
|
||||||
|
DiskTable,
|
||||||
|
CpuGraph,
|
||||||
|
NetGraph,
|
||||||
|
OldNetGraph,
|
||||||
|
ProcessManager,
|
||||||
|
BatteryTable,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- Old stuff below -----
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ScrollDirection {
|
||||||
|
// UP means scrolling up --- this usually DECREMENTS
|
||||||
|
Up,
|
||||||
|
// DOWN means scrolling down --- this usually INCREMENTS
|
||||||
|
Down,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ScrollDirection {
|
||||||
|
fn default() -> Self {
|
||||||
|
ScrollDirection::Down
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum CursorDirection {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// AppScrollWidgetState deals with fields for a scrollable app's current state.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct AppScrollWidgetState {
|
||||||
|
pub current_scroll_position: usize,
|
||||||
|
pub previous_scroll_position: usize,
|
||||||
|
pub scroll_direction: ScrollDirection,
|
||||||
|
pub table_state: TableState,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub enum KillSignal {
|
||||||
|
Cancel,
|
||||||
|
Kill(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for KillSignal {
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
fn default() -> Self {
|
||||||
|
KillSignal::Kill(15)
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn default() -> Self {
|
||||||
|
KillSignal::Kill(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct AppDeleteDialogState {
|
||||||
|
pub is_showing_dd: bool,
|
||||||
|
pub selected_signal: KillSignal,
|
||||||
|
/// tl x, tl y, br x, br y, index/signal
|
||||||
|
pub button_positions: Vec<(u16, u16, u16, u16, usize)>,
|
||||||
|
pub keyboard_signal_select: usize,
|
||||||
|
pub last_number_press: Option<Instant>,
|
||||||
|
pub scroll_pos: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AppHelpDialogState {
|
||||||
|
pub is_showing_help: bool,
|
||||||
|
pub scroll_state: ParagraphScrollState,
|
||||||
|
pub index_shortcuts: Vec<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AppHelpDialogState {
|
||||||
|
fn default() -> Self {
|
||||||
|
AppHelpDialogState {
|
||||||
|
is_showing_help: false,
|
||||||
|
scroll_state: ParagraphScrollState::default(),
|
||||||
|
index_shortcuts: vec![0; constants::HELP_TEXT.len()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Meant for canvas operations involving table column widths.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct CanvasTableWidthState {
|
||||||
|
pub desired_column_widths: Vec<u16>,
|
||||||
|
pub calculated_column_widths: Vec<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BasicTableWidgetState {
|
||||||
|
// Since this is intended (currently) to only be used for ONE widget, that's
|
||||||
|
// how it's going to be written. If we want to allow for multiple of these,
|
||||||
|
// then we can expand outwards with a normal BasicTableState and a hashmap
|
||||||
|
pub currently_displayed_widget_type: BottomWidgetType,
|
||||||
|
pub currently_displayed_widget_id: u64,
|
||||||
|
pub widget_id: i64,
|
||||||
|
pub left_tlc: Option<(u16, u16)>,
|
||||||
|
pub left_brc: Option<(u16, u16)>,
|
||||||
|
pub right_tlc: Option<(u16, u16)>,
|
||||||
|
pub right_brc: Option<(u16, u16)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ParagraphScrollState {
|
||||||
|
pub current_scroll_index: u16,
|
||||||
|
pub max_scroll_index: u16,
|
||||||
|
}
|
16
src/app/widgets/base.rs
Normal file
16
src/app/widgets/base.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//! A collection of basic components.
|
||||||
|
|
||||||
|
pub mod text_table;
|
||||||
|
pub use text_table::TextTable;
|
||||||
|
|
||||||
|
pub mod time_graph;
|
||||||
|
pub use time_graph::TimeGraph;
|
||||||
|
|
||||||
|
pub mod scrollable;
|
||||||
|
pub use scrollable::Scrollable;
|
||||||
|
|
||||||
|
pub mod text_input;
|
||||||
|
pub use text_input::TextInput;
|
||||||
|
|
||||||
|
pub mod carousel;
|
||||||
|
pub use carousel::Carousel;
|
Loading…
x
Reference in New Issue
Block a user