added gpu/sys/bt
This commit is contained in:
@@ -83,6 +83,9 @@ fn handle_request(module_name: &str, args: &[&str], state: &SharedState, config:
|
|||||||
"pool" | "btrfs" => crate::modules::btrfs::BtrfsModule.run(config, state, args),
|
"pool" | "btrfs" => crate::modules::btrfs::BtrfsModule.run(config, state, args),
|
||||||
"vol" => crate::modules::audio::AudioModule.run(config, state, &["sink", args.get(0).unwrap_or(&"show")]),
|
"vol" => crate::modules::audio::AudioModule.run(config, state, &["sink", args.get(0).unwrap_or(&"show")]),
|
||||||
"mic" => crate::modules::audio::AudioModule.run(config, state, &["source", args.get(0).unwrap_or(&"show")]),
|
"mic" => crate::modules::audio::AudioModule.run(config, state, &["source", args.get(0).unwrap_or(&"show")]),
|
||||||
|
"gpu" => crate::modules::gpu::GpuModule.run(config, state, args),
|
||||||
|
"sys" => crate::modules::sys::SysModule.run(config, state, args),
|
||||||
|
"bt" | "bluetooth" => crate::modules::bt::BtModule.run(config, state, args),
|
||||||
_ => {
|
_ => {
|
||||||
warn!("Received request for unknown module: '{}'", module_name);
|
warn!("Received request for unknown module: '{}'", module_name);
|
||||||
Err(anyhow::anyhow!("Unknown module: {}", module_name))
|
Err(anyhow::anyhow!("Unknown module: {}", module_name))
|
||||||
|
|||||||
19
src/main.rs
19
src/main.rs
@@ -53,6 +53,16 @@ enum Commands {
|
|||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
cycle: bool,
|
cycle: bool,
|
||||||
},
|
},
|
||||||
|
/// GPU usage, VRAM, and temp module
|
||||||
|
Gpu,
|
||||||
|
/// System load average and uptime
|
||||||
|
Sys,
|
||||||
|
/// Bluetooth audio device status
|
||||||
|
#[command(alias = "bluetooth")]
|
||||||
|
Bt {
|
||||||
|
#[arg(default_value = "show")]
|
||||||
|
action: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@@ -95,6 +105,15 @@ fn main() {
|
|||||||
let action = if *cycle { "cycle" } else { "show" };
|
let action = if *cycle { "cycle" } else { "show" };
|
||||||
handle_ipc_response(ipc::request_data("mic", &[action.to_string()]));
|
handle_ipc_response(ipc::request_data("mic", &[action.to_string()]));
|
||||||
}
|
}
|
||||||
|
Commands::Gpu => {
|
||||||
|
handle_ipc_response(ipc::request_data("gpu", &[]));
|
||||||
|
}
|
||||||
|
Commands::Sys => {
|
||||||
|
handle_ipc_response(ipc::request_data("sys", &[]));
|
||||||
|
}
|
||||||
|
Commands::Bt { action } => {
|
||||||
|
handle_ipc_response(ipc::request_data("bt", &[action.clone()]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ impl AudioModule {
|
|||||||
let output = Command::new("pactl").args(["list", "short", list_cmd]).output()?;
|
let output = Command::new("pactl").args(["list", "short", list_cmd]).output()?;
|
||||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
|
||||||
let mut devices: Vec<String> = stdout.lines()
|
let devices: Vec<String> = stdout.lines()
|
||||||
.filter_map(|l| {
|
.filter_map(|l| {
|
||||||
let parts: Vec<&str> = l.split_whitespace().collect();
|
let parts: Vec<&str> = l.split_whitespace().collect();
|
||||||
if parts.len() >= 2 {
|
if parts.len() >= 2 {
|
||||||
|
|||||||
117
src/modules/bt.rs
Normal file
117
src/modules/bt.rs
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
use crate::config::Config;
|
||||||
|
use crate::modules::WaybarModule;
|
||||||
|
use crate::output::WaybarOutput;
|
||||||
|
use crate::state::SharedState;
|
||||||
|
use anyhow::Result;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
pub struct BtModule;
|
||||||
|
|
||||||
|
impl WaybarModule for BtModule {
|
||||||
|
fn run(&self, _config: &Config, _state: &SharedState, args: &[&str]) -> Result<WaybarOutput> {
|
||||||
|
let action = args.first().unwrap_or(&"show");
|
||||||
|
|
||||||
|
if *action == "disconnect" {
|
||||||
|
if let Some(mac) = find_audio_device() {
|
||||||
|
let _ = Command::new("bluetoothctl").args(["disconnect", &mac]).output();
|
||||||
|
}
|
||||||
|
return Ok(WaybarOutput {
|
||||||
|
text: String::new(),
|
||||||
|
tooltip: None,
|
||||||
|
class: None,
|
||||||
|
percentage: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if bluetooth is powered on
|
||||||
|
if let Ok(output) = Command::new("bluetoothctl").arg("show").output() {
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
if stdout.contains("Powered: no") {
|
||||||
|
return Ok(WaybarOutput {
|
||||||
|
text: " Off".to_string(),
|
||||||
|
tooltip: Some("Bluetooth Disabled".to_string()),
|
||||||
|
class: Some("disabled".to_string()),
|
||||||
|
percentage: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(mac) = find_audio_device() {
|
||||||
|
let info_output = Command::new("bluetoothctl").args(["info", &mac]).output()?;
|
||||||
|
let info = String::from_utf8_lossy(&info_output.stdout);
|
||||||
|
|
||||||
|
let mut alias = mac.clone();
|
||||||
|
let mut battery = None;
|
||||||
|
let mut trusted = "no";
|
||||||
|
|
||||||
|
for line in info.lines() {
|
||||||
|
if line.contains("Alias:") {
|
||||||
|
alias = line.split("Alias:").nth(1).unwrap_or("").trim().to_string();
|
||||||
|
} else if line.contains("Battery Percentage:") {
|
||||||
|
if let Some(bat_str) = line.split('(').nth(1).and_then(|s| s.split(')').next()) {
|
||||||
|
battery = bat_str.parse::<u8>().ok();
|
||||||
|
}
|
||||||
|
} else if line.contains("Trusted: yes") {
|
||||||
|
trusted = "yes";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let tooltip = format!(
|
||||||
|
"{} | MAC: {}\nTrusted: {} | Bat: {}",
|
||||||
|
alias,
|
||||||
|
mac,
|
||||||
|
trusted,
|
||||||
|
battery.map(|b| format!("{}%", b)).unwrap_or_else(|| "N/A".to_string())
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(WaybarOutput {
|
||||||
|
text: format!("{} ", alias),
|
||||||
|
tooltip: Some(tooltip),
|
||||||
|
class: Some("connected".to_string()),
|
||||||
|
percentage: battery,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// No device connected but Bluetooth is on
|
||||||
|
Ok(WaybarOutput {
|
||||||
|
text: "".to_string(),
|
||||||
|
tooltip: Some("Bluetooth On (Disconnected)".to_string()),
|
||||||
|
class: Some("disconnected".to_string()),
|
||||||
|
percentage: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_audio_device() -> Option<String> {
|
||||||
|
// 1. Try to check if current default sink is a bluetooth device
|
||||||
|
if let Ok(output) = Command::new("pactl").arg("get-default-sink").output() {
|
||||||
|
let sink = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||||
|
if sink.starts_with("bluez_output.") {
|
||||||
|
let parts: Vec<&str> = sink.split('.').collect();
|
||||||
|
if parts.len() >= 2 {
|
||||||
|
return Some(parts[1].replace('_', ":"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Fallback: Search bluetoothctl for connected devices with Audio Sink UUID
|
||||||
|
if let Ok(output) = Command::new("bluetoothctl").args(["devices", "Connected"]).output() {
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
for line in stdout.lines() {
|
||||||
|
if line.starts_with("Device ") {
|
||||||
|
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||||
|
if parts.len() >= 2 {
|
||||||
|
let mac = parts[1];
|
||||||
|
if let Ok(info) = Command::new("bluetoothctl").args(["info", mac]).output() {
|
||||||
|
let info_str = String::from_utf8_lossy(&info.stdout);
|
||||||
|
if info_str.contains("0000110b-0000-1000-8000-00805f9b34fb") { // Audio Sink UUID
|
||||||
|
return Some(mac.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
64
src/modules/gpu.rs
Normal file
64
src/modules/gpu.rs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
use crate::config::Config;
|
||||||
|
use crate::modules::WaybarModule;
|
||||||
|
use crate::output::WaybarOutput;
|
||||||
|
use crate::state::SharedState;
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
pub struct GpuModule;
|
||||||
|
|
||||||
|
impl WaybarModule for GpuModule {
|
||||||
|
fn run(&self, _config: &Config, state: &SharedState, _args: &[&str]) -> Result<WaybarOutput> {
|
||||||
|
let (active, vendor, usage, vram_used, vram_total, temp, model) = {
|
||||||
|
if let Ok(state_lock) = state.read() {
|
||||||
|
(
|
||||||
|
state_lock.gpu.active,
|
||||||
|
state_lock.gpu.vendor.clone(),
|
||||||
|
state_lock.gpu.usage,
|
||||||
|
state_lock.gpu.vram_used,
|
||||||
|
state_lock.gpu.vram_total,
|
||||||
|
state_lock.gpu.temp,
|
||||||
|
state_lock.gpu.model.clone(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(false, String::from("Unknown"), 0.0, 0.0, 0.0, 0.0, String::from("Unknown"))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !active {
|
||||||
|
return Ok(WaybarOutput {
|
||||||
|
text: "No GPU".to_string(),
|
||||||
|
tooltip: None,
|
||||||
|
class: Some("normal".to_string()),
|
||||||
|
percentage: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let class = if usage > 95.0 {
|
||||||
|
"max"
|
||||||
|
} else if usage > 75.0 {
|
||||||
|
"high"
|
||||||
|
} else {
|
||||||
|
"normal"
|
||||||
|
};
|
||||||
|
|
||||||
|
let text = if vendor == "Intel" {
|
||||||
|
// Intel usually doesn't expose easy VRAM or Temp without root
|
||||||
|
format!("iGPU: {:.0}%", usage)
|
||||||
|
} else {
|
||||||
|
format!("{}: {:.0}% {:.1}/{:.1}GB {:.1}C", vendor, usage, vram_used, vram_total, temp)
|
||||||
|
};
|
||||||
|
|
||||||
|
let tooltip = if vendor == "Intel" {
|
||||||
|
format!("Model: {}\nApprox Usage: {:.0}%", model, usage)
|
||||||
|
} else {
|
||||||
|
format!("Model: {}\nUsage: {:.0}%\nVRAM: {:.1}/{:.1}GB\nTemp: {:.1}°C", model, usage, vram_used, vram_total, temp)
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(WaybarOutput {
|
||||||
|
text,
|
||||||
|
tooltip: Some(tooltip),
|
||||||
|
class: Some(class.to_string()),
|
||||||
|
percentage: Some(usage as u8),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,13 +17,13 @@ impl HardwareDaemon {
|
|||||||
pub fn poll(&mut self, state: SharedState) {
|
pub fn poll(&mut self, state: SharedState) {
|
||||||
self.sys.refresh_cpu_usage();
|
self.sys.refresh_cpu_usage();
|
||||||
self.sys.refresh_memory();
|
self.sys.refresh_memory();
|
||||||
|
self.sys.refresh_processes(sysinfo::ProcessesToUpdate::All, true);
|
||||||
self.components.refresh(true);
|
self.components.refresh(true);
|
||||||
|
|
||||||
let cpu_usage = self.sys.global_cpu_usage();
|
let cpu_usage = self.sys.global_cpu_usage();
|
||||||
let cpu_model = self.sys.cpus().first().map(|c| c.brand().to_string()).unwrap_or_else(|| "Unknown".to_string());
|
let cpu_model = self.sys.cpus().first().map(|c| c.brand().to_string()).unwrap_or_else(|| "Unknown".to_string());
|
||||||
|
|
||||||
// Try to find a reasonable CPU temperature
|
// Try to find a reasonable CPU temperature
|
||||||
// Often 'coretemp' or 'k10temp' depending on AMD/Intel
|
|
||||||
let mut cpu_temp = 0.0;
|
let mut cpu_temp = 0.0;
|
||||||
for component in &self.components {
|
for component in &self.components {
|
||||||
let label = component.label().to_lowercase();
|
let label = component.label().to_lowercase();
|
||||||
@@ -36,10 +36,13 @@ impl HardwareDaemon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let total_mem = self.sys.total_memory() as f64 / 1024.0 / 1024.0 / 1024.0;
|
let total_mem = self.sys.total_memory() as f64 / 1024.0 / 1024.0 / 1024.0;
|
||||||
// Accurate used memory matching htop/free (Total - Available)
|
|
||||||
let available_mem = self.sys.available_memory() as f64 / 1024.0 / 1024.0 / 1024.0;
|
let available_mem = self.sys.available_memory() as f64 / 1024.0 / 1024.0 / 1024.0;
|
||||||
let used_mem = total_mem - available_mem;
|
let used_mem = total_mem - available_mem;
|
||||||
|
|
||||||
|
let load_avg = System::load_average();
|
||||||
|
let uptime = System::uptime();
|
||||||
|
let process_count = self.sys.processes().len();
|
||||||
|
|
||||||
if let Ok(mut state_lock) = state.write() {
|
if let Ok(mut state_lock) = state.write() {
|
||||||
state_lock.cpu.usage = cpu_usage as f64;
|
state_lock.cpu.usage = cpu_usage as f64;
|
||||||
state_lock.cpu.temp = cpu_temp as f64;
|
state_lock.cpu.temp = cpu_temp as f64;
|
||||||
@@ -47,6 +50,104 @@ impl HardwareDaemon {
|
|||||||
|
|
||||||
state_lock.memory.total_gb = total_mem;
|
state_lock.memory.total_gb = total_mem;
|
||||||
state_lock.memory.used_gb = used_mem;
|
state_lock.memory.used_gb = used_mem;
|
||||||
|
|
||||||
|
state_lock.sys.load_1 = load_avg.one;
|
||||||
|
state_lock.sys.load_5 = load_avg.five;
|
||||||
|
state_lock.sys.load_15 = load_avg.fifteen;
|
||||||
|
state_lock.sys.uptime = uptime;
|
||||||
|
state_lock.sys.process_count = process_count;
|
||||||
|
|
||||||
|
self.poll_gpu(&mut state_lock.gpu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_gpu(&self, gpu: &mut crate::state::GpuState) {
|
||||||
|
gpu.active = false;
|
||||||
|
|
||||||
|
// 1. Try NVIDIA via nvidia-smi
|
||||||
|
if let Ok(output) = std::process::Command::new("nvidia-smi")
|
||||||
|
.args(["--query-gpu=utilization.gpu,memory.used,memory.total,temperature.gpu,name", "--format=csv,noheader,nounits"])
|
||||||
|
.output()
|
||||||
|
{
|
||||||
|
if output.status.success() {
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
if let Some(line) = stdout.lines().next() {
|
||||||
|
let parts: Vec<&str> = line.split(',').collect();
|
||||||
|
if parts.len() >= 5 {
|
||||||
|
gpu.active = true;
|
||||||
|
gpu.vendor = "NVIDIA".to_string();
|
||||||
|
gpu.usage = parts[0].trim().parse().unwrap_or(0.0);
|
||||||
|
gpu.vram_used = parts[1].trim().parse::<f64>().unwrap_or(0.0) / 1024.0; // from MB to GB
|
||||||
|
gpu.vram_total = parts[2].trim().parse::<f64>().unwrap_or(0.0) / 1024.0;
|
||||||
|
gpu.temp = parts[3].trim().parse().unwrap_or(0.0);
|
||||||
|
gpu.model = parts[4].trim().to_string();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over sysfs for AMD or Intel
|
||||||
|
for i in 0..=3 {
|
||||||
|
let base = format!("/sys/class/drm/card{}/device", i);
|
||||||
|
|
||||||
|
// 2. Try AMD
|
||||||
|
if let Ok(usage_str) = std::fs::read_to_string(format!("{}/gpu_busy_percent", base)) {
|
||||||
|
gpu.active = true;
|
||||||
|
gpu.vendor = "AMD".to_string();
|
||||||
|
gpu.usage = usage_str.trim().parse().unwrap_or(0.0);
|
||||||
|
|
||||||
|
if let Ok(mem_used) = std::fs::read_to_string(format!("{}/mem_info_vram_used", base)) {
|
||||||
|
gpu.vram_used = mem_used.trim().parse::<f64>().unwrap_or(0.0) / 1024.0 / 1024.0 / 1024.0;
|
||||||
|
}
|
||||||
|
if let Ok(mem_total) = std::fs::read_to_string(format!("{}/mem_info_vram_total", base)) {
|
||||||
|
gpu.vram_total = mem_total.trim().parse::<f64>().unwrap_or(0.0) / 1024.0 / 1024.0 / 1024.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(entries) = std::fs::read_dir(format!("{}/hwmon", base)) {
|
||||||
|
for entry in entries.flatten() {
|
||||||
|
let temp_path = entry.path().join("temp1_input");
|
||||||
|
if let Ok(temp_str) = std::fs::read_to_string(temp_path) {
|
||||||
|
gpu.temp = temp_str.trim().parse::<f64>().unwrap_or(0.0) / 1000.0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gpu.model = "AMD GPU".to_string();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Try Intel (iGPU)
|
||||||
|
let freq_path = if std::path::Path::new(&format!("{}/gt_cur_freq_mhz", base)).exists() {
|
||||||
|
Some(format!("{}/gt_cur_freq_mhz", base))
|
||||||
|
} else if std::path::Path::new(&format!("/sys/class/drm/card{}/gt_cur_freq_mhz", i)).exists() {
|
||||||
|
Some(format!("/sys/class/drm/card{}/gt_cur_freq_mhz", i))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(path) = freq_path {
|
||||||
|
if let Ok(freq_str) = std::fs::read_to_string(&path) {
|
||||||
|
gpu.active = true;
|
||||||
|
gpu.vendor = "Intel".to_string();
|
||||||
|
|
||||||
|
let cur_freq = freq_str.trim().parse::<f64>().unwrap_or(0.0);
|
||||||
|
let mut max_freq = 0.0;
|
||||||
|
|
||||||
|
let max_path = path.replace("gt_cur_freq_mhz", "gt_max_freq_mhz");
|
||||||
|
if let Ok(max_str) = std::fs::read_to_string(max_path) {
|
||||||
|
max_freq = max_str.trim().parse::<f64>().unwrap_or(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Approximate usage via frequency scaling
|
||||||
|
gpu.usage = if max_freq > 0.0 { (cur_freq / max_freq) * 100.0 } else { 0.0 };
|
||||||
|
gpu.temp = 0.0; // Intel iGPU temps are usually tied to CPU package temp
|
||||||
|
gpu.vram_used = 0.0; // iGPU shares system memory
|
||||||
|
gpu.vram_total = 0.0;
|
||||||
|
gpu.model = format!("Intel iGPU ({}MHz)", cur_freq);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ pub mod hardware;
|
|||||||
pub mod disk;
|
pub mod disk;
|
||||||
pub mod btrfs;
|
pub mod btrfs;
|
||||||
pub mod audio;
|
pub mod audio;
|
||||||
|
pub mod gpu;
|
||||||
|
pub mod sys;
|
||||||
|
pub mod bt;
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::output::WaybarOutput;
|
use crate::output::WaybarOutput;
|
||||||
|
|||||||
43
src/modules/sys.rs
Normal file
43
src/modules/sys.rs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
use crate::config::Config;
|
||||||
|
use crate::modules::WaybarModule;
|
||||||
|
use crate::output::WaybarOutput;
|
||||||
|
use crate::state::SharedState;
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
pub struct SysModule;
|
||||||
|
|
||||||
|
impl WaybarModule for SysModule {
|
||||||
|
fn run(&self, _config: &Config, state: &SharedState, _args: &[&str]) -> Result<WaybarOutput> {
|
||||||
|
let (load1, load5, load15, uptime_secs, process_count) = {
|
||||||
|
if let Ok(state_lock) = state.read() {
|
||||||
|
(
|
||||||
|
state_lock.sys.load_1,
|
||||||
|
state_lock.sys.load_5,
|
||||||
|
state_lock.sys.load_15,
|
||||||
|
state_lock.sys.uptime,
|
||||||
|
state_lock.sys.process_count,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(0.0, 0.0, 0.0, 0, 0)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let hours = uptime_secs / 3600;
|
||||||
|
let minutes = (uptime_secs % 3600) / 60;
|
||||||
|
let uptime_str = if hours > 0 {
|
||||||
|
format!("{}h {}m", hours, minutes)
|
||||||
|
} else {
|
||||||
|
format!("{}m", minutes)
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(WaybarOutput {
|
||||||
|
text: format!("UP: {} | LOAD: {:.2} {:.2} {:.2}", uptime_str, load1, load5, load15),
|
||||||
|
tooltip: Some(format!(
|
||||||
|
"Uptime: {}\nProcesses: {}\nLoad Avg: {:.2}, {:.2}, {:.2}",
|
||||||
|
uptime_str, process_count, load1, load5, load15
|
||||||
|
)),
|
||||||
|
class: Some("normal".to_string()),
|
||||||
|
percentage: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/state.rs
36
src/state.rs
@@ -5,6 +5,8 @@ pub struct AppState {
|
|||||||
pub network: NetworkState,
|
pub network: NetworkState,
|
||||||
pub cpu: CpuState,
|
pub cpu: CpuState,
|
||||||
pub memory: MemoryState,
|
pub memory: MemoryState,
|
||||||
|
pub sys: SysState,
|
||||||
|
pub gpu: GpuState,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
@@ -36,4 +38,38 @@ pub struct MemoryState {
|
|||||||
pub total_gb: f64,
|
pub total_gb: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
pub struct SysState {
|
||||||
|
pub load_1: f64,
|
||||||
|
pub load_5: f64,
|
||||||
|
pub load_15: f64,
|
||||||
|
pub uptime: u64,
|
||||||
|
pub process_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct GpuState {
|
||||||
|
pub active: bool,
|
||||||
|
pub vendor: String,
|
||||||
|
pub usage: f64,
|
||||||
|
pub vram_used: f64,
|
||||||
|
pub vram_total: f64,
|
||||||
|
pub temp: f64,
|
||||||
|
pub model: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GpuState {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
active: false,
|
||||||
|
vendor: String::from("Unknown"),
|
||||||
|
usage: 0.0,
|
||||||
|
vram_used: 0.0,
|
||||||
|
vram_total: 0.0,
|
||||||
|
temp: 0.0,
|
||||||
|
model: String::from("Unknown"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub type SharedState = Arc<RwLock<AppState>>;
|
pub type SharedState = Arc<RwLock<AppState>>;
|
||||||
|
|||||||
Reference in New Issue
Block a user