updated with custom configuration
This commit is contained in:
44
example.config.toml
Normal file
44
example.config.toml
Normal file
@@ -0,0 +1,44 @@
|
||||
# fluxo-rs example configuration
|
||||
# place this at ~/.config/fluxo/config.toml
|
||||
|
||||
# network module (net)
|
||||
# tokens: {interface}, {ip}, {rx}, {tx}, {rx:>5.2}, {tx:>5.2}
|
||||
[network]
|
||||
format = "{interface} ({ip}): {rx:>5.2} MB/s {tx:>5.2} MB/s"
|
||||
|
||||
# cpu module (cpu)
|
||||
# tokens: {usage}, {temp}, {usage:>4.1}, {temp:>4.1}
|
||||
[cpu]
|
||||
format = "CPU: {usage:>4.1}% {temp:>4.1}C"
|
||||
|
||||
# memory module (mem)
|
||||
# tokens: {used}, {total}, {used:>5.2}, {total:>5.2}
|
||||
[memory]
|
||||
format = "{used:>5.2}/{total:>5.2}GB"
|
||||
|
||||
# gpu module (gpu)
|
||||
# tokens: {usage}, {vram_used}, {vram_total}, {temp}, {usage:>3.0}, {vram_used:>4.1}, {vram_total:>4.1}, {temp:>4.1}
|
||||
[gpu]
|
||||
format_amd = "AMD: {usage:>3.0}% {vram_used:>4.1}/{vram_total:>4.1}GB {temp:>4.1}C"
|
||||
format_intel = "iGPU: {usage:>3.0}%"
|
||||
format_nvidia = "NV: {usage:>3.0}% {vram_used:>4.1}/{vram_total:>4.1}GB {temp:>4.1}C"
|
||||
|
||||
# system module (sys)
|
||||
# tokens: {uptime}, {load1}, {load5}, {load15}, {load1:>4.2}, {load5:>4.2}, {load15:>4.2}
|
||||
[sys]
|
||||
format = "UP: {uptime} | LOAD: {load1:>4.2} {load5:>4.2} {load15:>4.2}"
|
||||
|
||||
# disk module (disk <path>)
|
||||
# tokens: {mount}, {used}, {total}, {used:>5.1}, {total:>5.1}
|
||||
[disk]
|
||||
format = "{mount} {used:>5.1}/{total:>5.1}G"
|
||||
|
||||
# pool module (pool / btrfs)
|
||||
# tokens: {used}, {total}, {used:>4.0}, {total:>4.0}
|
||||
[pool]
|
||||
format = "{used:>4.0}G / {total:>4.0}G"
|
||||
|
||||
# power/battery module (power)
|
||||
# tokens: {percentage}, {icon}, {percentage:>3}
|
||||
[power]
|
||||
format = "{percentage:>3}% {icon}"
|
||||
111
src/config.rs
111
src/config.rs
@@ -6,6 +6,20 @@ use std::path::PathBuf;
|
||||
pub struct Config {
|
||||
#[serde(default)]
|
||||
pub network: NetworkConfig,
|
||||
#[serde(default)]
|
||||
pub cpu: CpuConfig,
|
||||
#[serde(default)]
|
||||
pub memory: MemoryConfig,
|
||||
#[serde(default)]
|
||||
pub gpu: GpuConfig,
|
||||
#[serde(default)]
|
||||
pub sys: SysConfig,
|
||||
#[serde(default)]
|
||||
pub disk: DiskConfig,
|
||||
#[serde(default)]
|
||||
pub pool: PoolConfig,
|
||||
#[serde(default)]
|
||||
pub power: PowerConfig,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -16,7 +30,102 @@ pub struct NetworkConfig {
|
||||
impl Default for NetworkConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
format: "{interface} ({ip}): {rx} MB/s {tx} MB/s".to_string(),
|
||||
format: "{interface} ({ip}): {rx:>5.2} MB/s {tx:>5.2} MB/s".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CpuConfig {
|
||||
pub format: String,
|
||||
}
|
||||
|
||||
impl Default for CpuConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
format: "CPU: {usage:>4.1}% {temp:>4.1}C".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct MemoryConfig {
|
||||
pub format: String,
|
||||
}
|
||||
|
||||
impl Default for MemoryConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
format: "{used:>5.2}/{total:>5.2}GB".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct GpuConfig {
|
||||
pub format_amd: String,
|
||||
pub format_intel: String,
|
||||
pub format_nvidia: String,
|
||||
}
|
||||
|
||||
impl Default for GpuConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
format_amd: "AMD: {usage:>3.0}% {vram_used:>4.1}/{vram_total:>4.1}GB {temp:>4.1}C".to_string(),
|
||||
format_intel: "iGPU: {usage:>3.0}%".to_string(),
|
||||
format_nvidia: "NV: {usage:>3.0}% {vram_used:>4.1}/{vram_total:>4.1}GB {temp:>4.1}C".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SysConfig {
|
||||
pub format: String,
|
||||
}
|
||||
|
||||
impl Default for SysConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
format: "UP: {uptime} | LOAD: {load1:>4.2} {load5:>4.2} {load15:>4.2}".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct DiskConfig {
|
||||
pub format: String,
|
||||
}
|
||||
|
||||
impl Default for DiskConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
format: "{mount} {used:>5.1}/{total:>5.1}G".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct PoolConfig {
|
||||
pub format: String,
|
||||
}
|
||||
|
||||
impl Default for PoolConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
format: "{used:>4.0}G / {total:>4.0}G".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct PowerConfig {
|
||||
pub format: String,
|
||||
}
|
||||
|
||||
impl Default for PowerConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
format: "{percentage:>3}% {icon}".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use sysinfo::Disks;
|
||||
pub struct BtrfsModule;
|
||||
|
||||
impl WaybarModule for BtrfsModule {
|
||||
fn run(&self, _config: &Config, _state: &SharedState, _args: &[&str]) -> Result<WaybarOutput> {
|
||||
fn run(&self, config: &Config, _state: &SharedState, _args: &[&str]) -> Result<WaybarOutput> {
|
||||
let disks = Disks::new_with_refreshed_list();
|
||||
let mut total_used: f64 = 0.0;
|
||||
let mut total_size: f64 = 0.0;
|
||||
@@ -43,8 +43,14 @@ impl WaybarModule for BtrfsModule {
|
||||
"normal"
|
||||
};
|
||||
|
||||
let text = config.pool.format
|
||||
.replace("{used:>4.0}", &format!("{:>4.0}", used_gb))
|
||||
.replace("{total:>4.0}", &format!("{:>4.0}", size_gb))
|
||||
.replace("{used}", &format!("{:.0}", used_gb))
|
||||
.replace("{total}", &format!("{:.0}", size_gb));
|
||||
|
||||
Ok(WaybarOutput {
|
||||
text: format!("{:.0}G / {:.0}G", used_gb, size_gb),
|
||||
text,
|
||||
tooltip: Some(format!("BTRFS Usage: {:.1}%", percentage)),
|
||||
class: Some(class.to_string()),
|
||||
percentage: Some(percentage as u8),
|
||||
|
||||
@@ -7,7 +7,7 @@ use anyhow::Result;
|
||||
pub struct CpuModule;
|
||||
|
||||
impl WaybarModule for CpuModule {
|
||||
fn run(&self, _config: &Config, state: &SharedState, _args: &[&str]) -> Result<WaybarOutput> {
|
||||
fn run(&self, config: &Config, state: &SharedState, _args: &[&str]) -> Result<WaybarOutput> {
|
||||
let (usage, temp, model) = {
|
||||
if let Ok(state_lock) = state.read() {
|
||||
(
|
||||
@@ -20,7 +20,11 @@ impl WaybarModule for CpuModule {
|
||||
}
|
||||
};
|
||||
|
||||
let text = format!("{:.1}% {:.1}C", usage, temp);
|
||||
let text = config.cpu.format
|
||||
.replace("{usage:>4.1}", &format!("{:>4.1}", usage))
|
||||
.replace("{temp:>4.1}", &format!("{:>4.1}", temp))
|
||||
.replace("{usage}", &format!("{:.1}", usage))
|
||||
.replace("{temp}", &format!("{:.1}", temp));
|
||||
|
||||
let class = if usage > 95.0 {
|
||||
"max"
|
||||
@@ -31,7 +35,7 @@ impl WaybarModule for CpuModule {
|
||||
};
|
||||
|
||||
Ok(WaybarOutput {
|
||||
text: format!("CPU: {}", text),
|
||||
text,
|
||||
tooltip: Some(model),
|
||||
class: Some(class.to_string()),
|
||||
percentage: Some(usage as u8),
|
||||
|
||||
@@ -8,7 +8,7 @@ use sysinfo::Disks;
|
||||
pub struct DiskModule;
|
||||
|
||||
impl WaybarModule for DiskModule {
|
||||
fn run(&self, _config: &Config, _state: &SharedState, args: &[&str]) -> Result<WaybarOutput> {
|
||||
fn run(&self, config: &Config, _state: &SharedState, args: &[&str]) -> Result<WaybarOutput> {
|
||||
let mountpoint = args.first().unwrap_or(&"/");
|
||||
|
||||
let disks = Disks::new_with_refreshed_list();
|
||||
@@ -32,8 +32,15 @@ impl WaybarModule for DiskModule {
|
||||
"normal"
|
||||
};
|
||||
|
||||
let text = config.disk.format
|
||||
.replace("{mount}", mountpoint)
|
||||
.replace("{used:>5.1}", &format!("{:>5.1}", used_gb))
|
||||
.replace("{total:>5.1}", &format!("{:>5.1}", total_gb))
|
||||
.replace("{used}", &format!("{:.1}", used_gb))
|
||||
.replace("{total}", &format!("{:.1}", total_gb));
|
||||
|
||||
return Ok(WaybarOutput {
|
||||
text: format!("{} {:.1}G/{:.1}G", mountpoint, used_gb, total_gb),
|
||||
text,
|
||||
tooltip: Some(format!("Used: {:.1}G\nTotal: {:.1}G\nFree: {:.1}G", used_gb, total_gb, free_gb)),
|
||||
class: Some(class.to_string()),
|
||||
percentage: Some(percentage as u8),
|
||||
|
||||
@@ -7,7 +7,7 @@ use anyhow::Result;
|
||||
pub struct GpuModule;
|
||||
|
||||
impl WaybarModule for GpuModule {
|
||||
fn run(&self, _config: &Config, state: &SharedState, _args: &[&str]) -> Result<WaybarOutput> {
|
||||
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() {
|
||||
(
|
||||
@@ -41,13 +41,22 @@ impl WaybarModule for GpuModule {
|
||||
"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 format_str = match vendor.as_str() {
|
||||
"Intel" => &config.gpu.format_intel,
|
||||
"NVIDIA" => &config.gpu.format_nvidia,
|
||||
_ => &config.gpu.format_amd,
|
||||
};
|
||||
|
||||
let text = format_str
|
||||
.replace("{usage:>3.0}", &format!("{:>3.0}", usage))
|
||||
.replace("{vram_used:>4.1}", &format!("{:>4.1}", vram_used))
|
||||
.replace("{vram_total:>4.1}", &format!("{:>4.1}", vram_total))
|
||||
.replace("{temp:>4.1}", &format!("{:>4.1}", temp))
|
||||
.replace("{usage}", &format!("{:.0}", usage))
|
||||
.replace("{vram_used}", &format!("{:.1}", vram_used))
|
||||
.replace("{vram_total}", &format!("{:.1}", vram_total))
|
||||
.replace("{temp}", &format!("{:.1}", temp));
|
||||
|
||||
let tooltip = if vendor == "Intel" {
|
||||
format!("Model: {}\nApprox Usage: {:.0}%", model, usage)
|
||||
} else {
|
||||
|
||||
@@ -4,6 +4,7 @@ use sysinfo::{Components, System};
|
||||
pub struct HardwareDaemon {
|
||||
sys: System,
|
||||
components: Components,
|
||||
gpu_vendor: Option<String>,
|
||||
}
|
||||
|
||||
impl HardwareDaemon {
|
||||
@@ -11,19 +12,17 @@ impl HardwareDaemon {
|
||||
let mut sys = System::new_all();
|
||||
sys.refresh_all();
|
||||
let components = Components::new_with_refreshed_list();
|
||||
Self { sys, components }
|
||||
Self { sys, components, gpu_vendor: None }
|
||||
}
|
||||
|
||||
pub fn poll(&mut self, state: SharedState) {
|
||||
self.sys.refresh_cpu_usage();
|
||||
self.sys.refresh_memory();
|
||||
self.sys.refresh_processes(sysinfo::ProcessesToUpdate::All, true);
|
||||
self.components.refresh(true);
|
||||
|
||||
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());
|
||||
|
||||
// Try to find a reasonable CPU temperature
|
||||
let mut cpu_temp = 0.0;
|
||||
for component in &self.components {
|
||||
let label = component.label().to_lowercase();
|
||||
@@ -41,7 +40,17 @@ impl HardwareDaemon {
|
||||
|
||||
let load_avg = System::load_average();
|
||||
let uptime = System::uptime();
|
||||
let process_count = self.sys.processes().len();
|
||||
|
||||
// Fast O(1) process count by reading loadavg instead of heavy sysinfo process refresh
|
||||
let mut process_count = 0;
|
||||
if let Ok(loadavg_str) = std::fs::read_to_string("/proc/loadavg") {
|
||||
let parts: Vec<&str> = loadavg_str.split_whitespace().collect();
|
||||
if parts.len() >= 4 {
|
||||
if let Some(total_procs) = parts[3].split('/').nth(1) {
|
||||
process_count = total_procs.parse().unwrap_or(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(mut state_lock) = state.write() {
|
||||
state_lock.cpu.usage = cpu_usage as f64;
|
||||
@@ -61,92 +70,100 @@ impl HardwareDaemon {
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_gpu(&self, gpu: &mut crate::state::GpuState) {
|
||||
fn poll_gpu(&mut 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;
|
||||
// Fast path: if we already detected NVIDIA, don't fallback to AMD/Intel scanning
|
||||
if self.gpu_vendor.as_deref() == Some("NVIDIA") || self.gpu_vendor.is_none() {
|
||||
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;
|
||||
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();
|
||||
self.gpu_vendor = Some("NVIDIA".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);
|
||||
// Fast path: if we detected AMD or Intel, scan sysfs
|
||||
if self.gpu_vendor.as_deref() == Some("AMD") || self.gpu_vendor.as_deref() == Some("Intel") || self.gpu_vendor.is_none() {
|
||||
for i in 0..=3 {
|
||||
let base = format!("/sys/class/drm/card{}/device", i);
|
||||
|
||||
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 self.gpu_vendor.as_deref() == Some("AMD") || self.gpu_vendor.is_none() {
|
||||
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();
|
||||
self.gpu_vendor = Some("AMD".to_string());
|
||||
return;
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
if self.gpu_vendor.as_deref() == Some("Intel") || self.gpu_vendor.is_none() {
|
||||
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);
|
||||
}
|
||||
|
||||
gpu.usage = if max_freq > 0.0 { (cur_freq / max_freq) * 100.0 } else { 0.0 };
|
||||
gpu.temp = 0.0;
|
||||
gpu.vram_used = 0.0;
|
||||
gpu.vram_total = 0.0;
|
||||
gpu.model = format!("Intel iGPU ({}MHz)", cur_freq);
|
||||
self.gpu_vendor = Some("Intel".to_string());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use anyhow::Result;
|
||||
pub struct MemoryModule;
|
||||
|
||||
impl WaybarModule for MemoryModule {
|
||||
fn run(&self, _config: &Config, state: &SharedState, _args: &[&str]) -> Result<WaybarOutput> {
|
||||
fn run(&self, config: &Config, state: &SharedState, _args: &[&str]) -> Result<WaybarOutput> {
|
||||
let (used_gb, total_gb) = {
|
||||
if let Ok(state_lock) = state.read() {
|
||||
(
|
||||
@@ -21,6 +21,12 @@ impl WaybarModule for MemoryModule {
|
||||
|
||||
let ratio = if total_gb > 0.0 { (used_gb / total_gb) * 100.0 } else { 0.0 };
|
||||
|
||||
let text = config.memory.format
|
||||
.replace("{used:>5.2}", &format!("{:>5.2}", used_gb))
|
||||
.replace("{total:>5.2}", &format!("{:>5.2}", total_gb))
|
||||
.replace("{used}", &format!("{:.2}", used_gb))
|
||||
.replace("{total}", &format!("{:.2}", total_gb));
|
||||
|
||||
let class = if ratio > 95.0 {
|
||||
"max"
|
||||
} else if ratio > 75.0 {
|
||||
@@ -30,7 +36,7 @@ impl WaybarModule for MemoryModule {
|
||||
};
|
||||
|
||||
Ok(WaybarOutput {
|
||||
text: format!("{:.2}/{:.2}GB", used_gb, total_gb),
|
||||
text,
|
||||
tooltip: None,
|
||||
class: Some(class.to_string()),
|
||||
percentage: Some(ratio as u8),
|
||||
|
||||
@@ -13,6 +13,8 @@ pub struct NetworkDaemon {
|
||||
last_time: u64,
|
||||
last_rx_bytes: u64,
|
||||
last_tx_bytes: u64,
|
||||
cached_interface: Option<String>,
|
||||
cached_ip: Option<String>,
|
||||
}
|
||||
|
||||
impl NetworkDaemon {
|
||||
@@ -21,40 +23,57 @@ impl NetworkDaemon {
|
||||
last_time: 0,
|
||||
last_rx_bytes: 0,
|
||||
last_tx_bytes: 0,
|
||||
cached_interface: None,
|
||||
cached_ip: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll(&mut self, state: SharedState) {
|
||||
if let Ok(interface) = get_primary_interface() {
|
||||
if !interface.is_empty() {
|
||||
let time_now = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_secs();
|
||||
// Cache invalidation: if the interface directory doesn't exist, clear cache
|
||||
if let Some(ref iface) = self.cached_interface {
|
||||
if !std::path::Path::new(&format!("/sys/class/net/{}", iface)).exists() {
|
||||
self.cached_interface = None;
|
||||
self.cached_ip = None;
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok((rx_bytes_now, tx_bytes_now)) = get_bytes(&interface) {
|
||||
if self.last_time > 0 && time_now > self.last_time {
|
||||
let time_diff = time_now - self.last_time;
|
||||
let rx_bps = (rx_bytes_now.saturating_sub(self.last_rx_bytes)) / time_diff;
|
||||
let tx_bps = (tx_bytes_now.saturating_sub(self.last_tx_bytes)) / time_diff;
|
||||
|
||||
let rx_mbps = (rx_bps as f64) / 1024.0 / 1024.0;
|
||||
let tx_mbps = (tx_bps as f64) / 1024.0 / 1024.0;
|
||||
|
||||
debug!(interface, rx = rx_mbps, tx = tx_mbps, "Network stats updated");
|
||||
|
||||
if let Ok(mut state_lock) = state.write() {
|
||||
state_lock.network.rx_mbps = rx_mbps;
|
||||
state_lock.network.tx_mbps = tx_mbps;
|
||||
}
|
||||
}
|
||||
|
||||
self.last_time = time_now;
|
||||
self.last_rx_bytes = rx_bytes_now;
|
||||
self.last_tx_bytes = tx_bytes_now;
|
||||
// Re-detect interface if needed
|
||||
if self.cached_interface.is_none() {
|
||||
if let Ok(iface) = get_primary_interface() {
|
||||
if !iface.is_empty() {
|
||||
self.cached_interface = Some(iface.clone());
|
||||
self.cached_ip = get_ip_address(&iface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref interface) = self.cached_interface {
|
||||
let time_now = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_secs();
|
||||
|
||||
if let Ok((rx_bytes_now, tx_bytes_now)) = get_bytes(interface) {
|
||||
if self.last_time > 0 && time_now > self.last_time {
|
||||
let time_diff = time_now - self.last_time;
|
||||
let rx_bps = (rx_bytes_now.saturating_sub(self.last_rx_bytes)) / time_diff;
|
||||
let tx_bps = (tx_bytes_now.saturating_sub(self.last_tx_bytes)) / time_diff;
|
||||
|
||||
let rx_mbps = (rx_bps as f64) / 1024.0 / 1024.0;
|
||||
let tx_mbps = (tx_bps as f64) / 1024.0 / 1024.0;
|
||||
|
||||
if let Ok(mut state_lock) = state.write() {
|
||||
state_lock.network.rx_mbps = rx_mbps;
|
||||
state_lock.network.tx_mbps = tx_mbps;
|
||||
}
|
||||
}
|
||||
|
||||
self.last_time = time_now;
|
||||
self.last_rx_bytes = rx_bytes_now;
|
||||
self.last_tx_bytes = tx_bytes_now;
|
||||
} else {
|
||||
warn!("No primary network interface found during poll");
|
||||
// Read failed, might be down
|
||||
self.cached_interface = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,6 +106,8 @@ impl WaybarModule for NetworkModule {
|
||||
.format
|
||||
.replace("{interface}", &interface)
|
||||
.replace("{ip}", &ip)
|
||||
.replace("{rx:>5.2}", &format!("{:>5.2}", rx_mbps))
|
||||
.replace("{tx:>5.2}", &format!("{:>5.2}", tx_mbps))
|
||||
.replace("{rx}", &format!("{:.2}", rx_mbps))
|
||||
.replace("{tx}", &format!("{:.2}", tx_mbps));
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::fs;
|
||||
pub struct PowerModule;
|
||||
|
||||
impl WaybarModule for PowerModule {
|
||||
fn run(&self, _config: &Config, _state: &SharedState, _args: &[&str]) -> Result<WaybarOutput> {
|
||||
fn run(&self, config: &Config, _state: &SharedState, _args: &[&str]) -> Result<WaybarOutput> {
|
||||
let critical_threshold = 15;
|
||||
let warning_threshold = 50;
|
||||
|
||||
@@ -24,7 +24,7 @@ impl WaybarModule for PowerModule {
|
||||
}
|
||||
}
|
||||
|
||||
// Check AC status as fallback or TLP proxy
|
||||
// Check AC status
|
||||
let mut ac_online = false;
|
||||
if let Ok(entries) = fs::read_dir("/sys/class/power_supply") {
|
||||
for entry in entries.flatten() {
|
||||
@@ -59,7 +59,6 @@ impl WaybarModule for PowerModule {
|
||||
}
|
||||
};
|
||||
|
||||
// Read battery capacity and status
|
||||
let capacity_str = fs::read_to_string(bat_path.join("capacity")).unwrap_or_else(|_| "0".to_string());
|
||||
let percentage: u8 = capacity_str.trim().parse().unwrap_or(0);
|
||||
let status_str = fs::read_to_string(bat_path.join("status")).unwrap_or_else(|_| "Unknown".to_string());
|
||||
@@ -90,8 +89,13 @@ impl WaybarModule for PowerModule {
|
||||
)
|
||||
};
|
||||
|
||||
let text = config.power.format
|
||||
.replace("{percentage:>3}", &format!("{:>3}", percentage))
|
||||
.replace("{percentage}", &format!("{}", percentage))
|
||||
.replace("{icon}", icon);
|
||||
|
||||
Ok(WaybarOutput {
|
||||
text: format!("{}% {}", percentage, icon),
|
||||
text,
|
||||
tooltip: Some(tooltip),
|
||||
class: Some(class.to_string()),
|
||||
percentage: Some(percentage),
|
||||
|
||||
@@ -7,7 +7,7 @@ use anyhow::Result;
|
||||
pub struct SysModule;
|
||||
|
||||
impl WaybarModule for SysModule {
|
||||
fn run(&self, _config: &Config, state: &SharedState, _args: &[&str]) -> Result<WaybarOutput> {
|
||||
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() {
|
||||
(
|
||||
@@ -30,8 +30,17 @@ impl WaybarModule for SysModule {
|
||||
format!("{}m", minutes)
|
||||
};
|
||||
|
||||
let text = config.sys.format
|
||||
.replace("{uptime}", &uptime_str)
|
||||
.replace("{load1:>4.2}", &format!("{:>4.2}", load1))
|
||||
.replace("{load5:>4.2}", &format!("{:>4.2}", load5))
|
||||
.replace("{load15:>4.2}", &format!("{:>4.2}", load15))
|
||||
.replace("{load1}", &format!("{:.2}", load1))
|
||||
.replace("{load5}", &format!("{:.2}", load5))
|
||||
.replace("{load15}", &format!("{:.2}", load15));
|
||||
|
||||
Ok(WaybarOutput {
|
||||
text: format!("UP: {} | LOAD: {:.2} {:.2} {:.2}", uptime_str, load1, load5, load15),
|
||||
text,
|
||||
tooltip: Some(format!(
|
||||
"Uptime: {}\nProcesses: {}\nLoad Avg: {:.2}, {:.2}, {:.2}",
|
||||
uptime_str, process_count, load1, load5, load15
|
||||
|
||||
Reference in New Issue
Block a user