improved performance
This commit is contained in:
+72
-39
@@ -2,7 +2,7 @@ use crate::config::Config;
|
||||
use crate::modules::WaybarModule;
|
||||
use crate::output::WaybarOutput;
|
||||
use crate::state::SharedState;
|
||||
use crate::utils::{format_template, TokenValue};
|
||||
use crate::utils::{TokenValue, format_template, run_command};
|
||||
use anyhow::{Result, anyhow};
|
||||
use std::process::Command;
|
||||
|
||||
@@ -16,30 +16,30 @@ impl WaybarModule for AudioModule {
|
||||
match *action {
|
||||
"cycle" => {
|
||||
self.cycle_device(target_type)?;
|
||||
return Ok(WaybarOutput {
|
||||
Ok(WaybarOutput {
|
||||
text: String::new(),
|
||||
tooltip: None,
|
||||
class: None,
|
||||
percentage: None,
|
||||
});
|
||||
}
|
||||
"show" | _ => {
|
||||
self.get_status(config, target_type)
|
||||
})
|
||||
}
|
||||
"show" => self.get_status(config, target_type),
|
||||
other => Err(anyhow!("Unknown audio action: '{}'", other)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AudioModule {
|
||||
fn get_status(&self, config: &Config, target_type: &str) -> Result<WaybarOutput> {
|
||||
let target = if target_type == "sink" { "@DEFAULT_AUDIO_SINK@" } else { "@DEFAULT_AUDIO_SOURCE@" };
|
||||
let target = if target_type == "sink" {
|
||||
"@DEFAULT_AUDIO_SINK@"
|
||||
} else {
|
||||
"@DEFAULT_AUDIO_SOURCE@"
|
||||
};
|
||||
|
||||
let output = Command::new("wpctl")
|
||||
.args(["get-volume", target])
|
||||
.output()?;
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
|
||||
let parts: Vec<&str> = stdout.trim().split_whitespace().collect();
|
||||
let stdout = run_command("wpctl", &["get-volume", target])?;
|
||||
|
||||
let parts: Vec<&str> = stdout.split_whitespace().collect();
|
||||
if parts.len() < 2 {
|
||||
return Err(anyhow!("Could not parse wpctl output: {}", stdout));
|
||||
}
|
||||
@@ -58,31 +58,43 @@ impl AudioModule {
|
||||
|
||||
let (text, class) = if muted {
|
||||
let icon = if target_type == "sink" { "" } else { "" };
|
||||
let format_str = if target_type == "sink" { &config.audio.format_sink_muted } else { &config.audio.format_source_muted };
|
||||
let format_str = if target_type == "sink" {
|
||||
&config.audio.format_sink_muted
|
||||
} else {
|
||||
&config.audio.format_source_muted
|
||||
};
|
||||
let t = format_template(
|
||||
format_str,
|
||||
&[
|
||||
("name", TokenValue::String(&name)),
|
||||
("icon", TokenValue::String(icon)),
|
||||
]
|
||||
],
|
||||
);
|
||||
(t, "muted")
|
||||
} else {
|
||||
let icon = if target_type == "sink" {
|
||||
if display_vol <= 30 { "" }
|
||||
else if display_vol <= 60 { "" }
|
||||
else { "" }
|
||||
if display_vol <= 30 {
|
||||
""
|
||||
} else if display_vol <= 60 {
|
||||
""
|
||||
} else {
|
||||
""
|
||||
}
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let format_str = if target_type == "sink" { &config.audio.format_sink_unmuted } else { &config.audio.format_source_unmuted };
|
||||
let format_str = if target_type == "sink" {
|
||||
&config.audio.format_sink_unmuted
|
||||
} else {
|
||||
&config.audio.format_source_unmuted
|
||||
};
|
||||
let t = format_template(
|
||||
format_str,
|
||||
&[
|
||||
("name", TokenValue::String(&name)),
|
||||
("icon", TokenValue::String(icon)),
|
||||
("volume", TokenValue::Int(display_vol as i64)),
|
||||
]
|
||||
],
|
||||
);
|
||||
(t, "unmuted")
|
||||
};
|
||||
@@ -96,19 +108,26 @@ impl AudioModule {
|
||||
}
|
||||
|
||||
fn get_description(&self, target_type: &str) -> Result<String> {
|
||||
let info_output = Command::new("pactl").arg("info").output()?;
|
||||
let info_stdout = String::from_utf8_lossy(&info_output.stdout);
|
||||
let search_key = if target_type == "sink" { "Default Sink:" } else { "Default Source:" };
|
||||
|
||||
let default_dev = info_stdout.lines()
|
||||
let info_stdout = run_command("pactl", &["info"])?;
|
||||
let search_key = if target_type == "sink" {
|
||||
"Default Sink:"
|
||||
} else {
|
||||
"Default Source:"
|
||||
};
|
||||
|
||||
let default_dev = info_stdout
|
||||
.lines()
|
||||
.find(|l| l.contains(search_key))
|
||||
.and_then(|l| l.split(':').nth(1))
|
||||
.map(|s| s.trim())
|
||||
.ok_or_else(|| anyhow!("Default {} not found", target_type))?;
|
||||
|
||||
let list_cmd = if target_type == "sink" { "sinks" } else { "sources" };
|
||||
let list_output = Command::new("pactl").args(["list", list_cmd]).output()?;
|
||||
let list_stdout = String::from_utf8_lossy(&list_output.stdout);
|
||||
let list_cmd = if target_type == "sink" {
|
||||
"sinks"
|
||||
} else {
|
||||
"sources"
|
||||
};
|
||||
let list_stdout = run_command("pactl", &["list", list_cmd])?;
|
||||
|
||||
let mut current_name = String::new();
|
||||
for line in list_stdout.lines() {
|
||||
@@ -124,11 +143,15 @@ impl AudioModule {
|
||||
}
|
||||
|
||||
fn cycle_device(&self, target_type: &str) -> Result<()> {
|
||||
let list_cmd = if target_type == "sink" { "sinks" } else { "sources" };
|
||||
let output = Command::new("pactl").args(["list", "short", list_cmd]).output()?;
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let list_cmd = if target_type == "sink" {
|
||||
"sinks"
|
||||
} else {
|
||||
"sources"
|
||||
};
|
||||
let stdout = run_command("pactl", &["list", "short", list_cmd])?;
|
||||
|
||||
let devices: Vec<String> = stdout.lines()
|
||||
let devices: Vec<String> = stdout
|
||||
.lines()
|
||||
.filter_map(|l| {
|
||||
let parts: Vec<&str> = l.split_whitespace().collect();
|
||||
if parts.len() >= 2 {
|
||||
@@ -144,13 +167,19 @@ impl AudioModule {
|
||||
})
|
||||
.collect();
|
||||
|
||||
if devices.is_empty() { return Ok(()); }
|
||||
if devices.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let info_output = Command::new("pactl").arg("info").output()?;
|
||||
let info_stdout = String::from_utf8_lossy(&info_output.stdout);
|
||||
let search_key = if target_type == "sink" { "Default Sink:" } else { "Default Source:" };
|
||||
|
||||
let current_dev = info_stdout.lines()
|
||||
let info_stdout = run_command("pactl", &["info"])?;
|
||||
let search_key = if target_type == "sink" {
|
||||
"Default Sink:"
|
||||
} else {
|
||||
"Default Source:"
|
||||
};
|
||||
|
||||
let current_dev = info_stdout
|
||||
.lines()
|
||||
.find(|l| l.contains(search_key))
|
||||
.and_then(|l| l.split(':').nth(1))
|
||||
.map(|s| s.trim())
|
||||
@@ -160,7 +189,11 @@ impl AudioModule {
|
||||
let next_index = (current_index + 1) % devices.len();
|
||||
let next_dev = &devices[next_index];
|
||||
|
||||
let set_cmd = if target_type == "sink" { "set-default-sink" } else { "set-default-source" };
|
||||
let set_cmd = if target_type == "sink" {
|
||||
"set-default-sink"
|
||||
} else {
|
||||
"set-default-source"
|
||||
};
|
||||
Command::new("pactl").args([set_cmd, next_dev]).status()?;
|
||||
|
||||
Ok(())
|
||||
|
||||
+15
-15
@@ -2,7 +2,7 @@ use crate::config::Config;
|
||||
use crate::modules::WaybarModule;
|
||||
use crate::output::WaybarOutput;
|
||||
use crate::state::SharedState;
|
||||
use crate::utils::{format_template, TokenValue};
|
||||
use crate::utils::{TokenValue, format_template, run_command};
|
||||
use anyhow::Result;
|
||||
use std::process::Command;
|
||||
|
||||
@@ -14,7 +14,9 @@ impl WaybarModule for BtModule {
|
||||
|
||||
if *action == "disconnect" {
|
||||
if let Some(mac) = find_audio_device() {
|
||||
let _ = Command::new("bluetoothctl").args(["disconnect", &mac]).output();
|
||||
let _ = Command::new("bluetoothctl")
|
||||
.args(["disconnect", &mac])
|
||||
.output();
|
||||
}
|
||||
return Ok(WaybarOutput {
|
||||
text: String::new(),
|
||||
@@ -24,8 +26,7 @@ impl WaybarModule for BtModule {
|
||||
});
|
||||
}
|
||||
|
||||
if let Ok(output) = Command::new("bluetoothctl").arg("show").output() {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
if let Ok(stdout) = run_command("bluetoothctl", &["show"]) {
|
||||
if stdout.contains("Powered: no") {
|
||||
return Ok(WaybarOutput {
|
||||
text: config.bt.format_disabled.clone(),
|
||||
@@ -37,8 +38,7 @@ impl WaybarModule for BtModule {
|
||||
}
|
||||
|
||||
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 info = run_command("bluetoothctl", &["info", &mac])?;
|
||||
|
||||
let mut alias = mac.clone();
|
||||
let mut battery = None;
|
||||
@@ -48,7 +48,8 @@ impl WaybarModule for BtModule {
|
||||
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()) {
|
||||
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") {
|
||||
@@ -61,12 +62,14 @@ impl WaybarModule for BtModule {
|
||||
alias,
|
||||
mac,
|
||||
trusted,
|
||||
battery.map(|b| format!("{}%", b)).unwrap_or_else(|| "N/A".to_string())
|
||||
battery
|
||||
.map(|b| format!("{}%", b))
|
||||
.unwrap_or_else(|| "N/A".to_string())
|
||||
);
|
||||
|
||||
let text = format_template(
|
||||
&config.bt.format_connected,
|
||||
&[("alias", TokenValue::String(&alias))]
|
||||
&[("alias", TokenValue::String(&alias))],
|
||||
);
|
||||
|
||||
Ok(WaybarOutput {
|
||||
@@ -87,8 +90,7 @@ impl WaybarModule for BtModule {
|
||||
}
|
||||
|
||||
fn find_audio_device() -> Option<String> {
|
||||
if let Ok(output) = Command::new("pactl").arg("get-default-sink").output() {
|
||||
let sink = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||
if let Ok(sink) = run_command("pactl", &["get-default-sink"]) {
|
||||
if sink.starts_with("bluez_output.") {
|
||||
let parts: Vec<&str> = sink.split('.').collect();
|
||||
if parts.len() >= 2 {
|
||||
@@ -97,15 +99,13 @@ fn find_audio_device() -> Option<String> {
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(output) = Command::new("bluetoothctl").args(["devices", "Connected"]).output() {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
if let Ok(stdout) = run_command("bluetoothctl", &["devices", "Connected"]) {
|
||||
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 let Ok(info_str) = run_command("bluetoothctl", &["info", mac]) {
|
||||
if info_str.contains("0000110b-0000-1000-8000-00805f9b34fb") {
|
||||
return Some(mac.to_string());
|
||||
}
|
||||
|
||||
+12
-8
@@ -2,22 +2,26 @@ use crate::config::Config;
|
||||
use crate::modules::WaybarModule;
|
||||
use crate::output::WaybarOutput;
|
||||
use crate::state::SharedState;
|
||||
use crate::utils::{format_template, TokenValue};
|
||||
use crate::utils::{TokenValue, format_template};
|
||||
use anyhow::Result;
|
||||
use sysinfo::Disks;
|
||||
|
||||
pub struct BtrfsModule;
|
||||
|
||||
impl WaybarModule for BtrfsModule {
|
||||
fn run(&self, config: &Config, _state: &SharedState, _args: &[&str]) -> Result<WaybarOutput> {
|
||||
let disks = Disks::new_with_refreshed_list();
|
||||
fn run(&self, config: &Config, state: &SharedState, _args: &[&str]) -> Result<WaybarOutput> {
|
||||
let disks = if let Ok(s) = state.read() {
|
||||
s.disks.clone()
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("Failed to read state"));
|
||||
};
|
||||
|
||||
let mut total_used: f64 = 0.0;
|
||||
let mut total_size: f64 = 0.0;
|
||||
|
||||
for disk in &disks {
|
||||
if disk.file_system().to_string_lossy().to_lowercase().contains("btrfs") {
|
||||
let size = disk.total_space() as f64;
|
||||
let available = disk.available_space() as f64;
|
||||
if disk.filesystem.contains("btrfs") {
|
||||
let size = disk.total_bytes as f64;
|
||||
let available = disk.available_bytes as f64;
|
||||
total_size += size;
|
||||
total_used += size - available;
|
||||
}
|
||||
@@ -49,7 +53,7 @@ impl WaybarModule for BtrfsModule {
|
||||
&[
|
||||
("used", TokenValue::Float(used_gb)),
|
||||
("total", TokenValue::Float(size_gb)),
|
||||
]
|
||||
],
|
||||
);
|
||||
|
||||
Ok(WaybarOutput {
|
||||
|
||||
+41
-16
@@ -2,7 +2,7 @@ use crate::config::Config;
|
||||
use crate::modules::WaybarModule;
|
||||
use crate::output::WaybarOutput;
|
||||
use crate::state::SharedState;
|
||||
use crate::utils::{format_template, TokenValue};
|
||||
use crate::utils::{TokenValue, format_template};
|
||||
use anyhow::Result;
|
||||
use std::process::Command;
|
||||
|
||||
@@ -17,39 +17,54 @@ impl WaybarModule for BudsModule {
|
||||
"cycle_anc" => {
|
||||
let output = Command::new("pbpctrl").args(["get", "anc"]).output()?;
|
||||
let current_mode = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||
|
||||
|
||||
let next_mode = match current_mode.as_str() {
|
||||
"active" => "aware",
|
||||
"aware" => "off",
|
||||
_ => "active",
|
||||
};
|
||||
|
||||
Command::new("pbpctrl").args(["set", "anc", next_mode]).status()?;
|
||||
|
||||
Command::new("pbpctrl")
|
||||
.args(["set", "anc", next_mode])
|
||||
.status()?;
|
||||
return Ok(WaybarOutput {
|
||||
text: String::new(),
|
||||
tooltip: None, class: None, percentage: None,
|
||||
tooltip: None,
|
||||
class: None,
|
||||
percentage: None,
|
||||
});
|
||||
}
|
||||
"connect" => {
|
||||
Command::new("bluetoothctl").args(["connect", mac]).status()?;
|
||||
Command::new("bluetoothctl")
|
||||
.args(["connect", mac])
|
||||
.status()?;
|
||||
return Ok(WaybarOutput {
|
||||
text: String::new(),
|
||||
tooltip: None, class: None, percentage: None,
|
||||
tooltip: None,
|
||||
class: None,
|
||||
percentage: None,
|
||||
});
|
||||
}
|
||||
"disconnect" => {
|
||||
Command::new("bluetoothctl").args(["disconnect", mac]).status()?;
|
||||
Command::new("bluetoothctl")
|
||||
.args(["disconnect", mac])
|
||||
.status()?;
|
||||
return Ok(WaybarOutput {
|
||||
text: String::new(),
|
||||
tooltip: None, class: None, percentage: None,
|
||||
tooltip: None,
|
||||
class: None,
|
||||
percentage: None,
|
||||
});
|
||||
}
|
||||
"show" | _ => {}
|
||||
"show" => {}
|
||||
other => {
|
||||
return Err(anyhow::anyhow!("Unknown buds action: '{}'", other));
|
||||
}
|
||||
}
|
||||
|
||||
let bt_info = Command::new("bluetoothctl").args(["info", mac]).output()?;
|
||||
let bt_str = String::from_utf8_lossy(&bt_info.stdout);
|
||||
|
||||
|
||||
if !bt_str.contains("Connected: yes") {
|
||||
return Ok(WaybarOutput {
|
||||
text: config.buds.format_disconnected.clone(),
|
||||
@@ -68,7 +83,7 @@ impl WaybarModule for BudsModule {
|
||||
percentage: None,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
let bat_result = bat_cmd.unwrap();
|
||||
let bat_output = String::from_utf8_lossy(&bat_result.stdout);
|
||||
let mut left_bud = "unknown";
|
||||
@@ -85,12 +100,22 @@ impl WaybarModule for BudsModule {
|
||||
if left_bud == "unknown" && right_bud == "unknown" {
|
||||
return Ok(WaybarOutput {
|
||||
text: "{}".to_string(),
|
||||
tooltip: None, class: None, percentage: None,
|
||||
tooltip: None,
|
||||
class: None,
|
||||
percentage: None,
|
||||
});
|
||||
}
|
||||
|
||||
let left_display = if left_bud == "unknown" { "---".to_string() } else { format!("{}%", left_bud) };
|
||||
let right_display = if right_bud == "unknown" { "---".to_string() } else { format!("{}%", right_bud) };
|
||||
let left_display = if left_bud == "unknown" {
|
||||
"---".to_string()
|
||||
} else {
|
||||
format!("{}%", left_bud)
|
||||
};
|
||||
let right_display = if right_bud == "unknown" {
|
||||
"---".to_string()
|
||||
} else {
|
||||
format!("{}%", right_bud)
|
||||
};
|
||||
|
||||
let anc_cmd = Command::new("pbpctrl").args(["get", "anc"]).output()?;
|
||||
let current_mode = String::from_utf8_lossy(&anc_cmd.stdout).trim().to_string();
|
||||
@@ -108,7 +133,7 @@ impl WaybarModule for BudsModule {
|
||||
("left", TokenValue::String(&left_display)),
|
||||
("right", TokenValue::String(&right_display)),
|
||||
("anc", TokenValue::String(anc_icon)),
|
||||
]
|
||||
],
|
||||
);
|
||||
|
||||
Ok(WaybarOutput {
|
||||
|
||||
+3
-3
@@ -2,7 +2,7 @@ use crate::config::Config;
|
||||
use crate::modules::WaybarModule;
|
||||
use crate::output::WaybarOutput;
|
||||
use crate::state::SharedState;
|
||||
use crate::utils::{format_template, TokenValue};
|
||||
use crate::utils::{TokenValue, format_template};
|
||||
use anyhow::Result;
|
||||
|
||||
pub struct CpuModule;
|
||||
@@ -26,9 +26,9 @@ impl WaybarModule for CpuModule {
|
||||
&[
|
||||
("usage", TokenValue::Float(usage)),
|
||||
("temp", TokenValue::Float(temp)),
|
||||
]
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
let class = if usage > 95.0 {
|
||||
"max"
|
||||
} else if usage > 75.0 {
|
||||
|
||||
+24
-13
@@ -2,28 +2,36 @@ use crate::config::Config;
|
||||
use crate::modules::WaybarModule;
|
||||
use crate::output::WaybarOutput;
|
||||
use crate::state::SharedState;
|
||||
use crate::utils::{format_template, TokenValue};
|
||||
use crate::utils::{TokenValue, format_template};
|
||||
use anyhow::Result;
|
||||
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();
|
||||
|
||||
let disks = if let Ok(s) = state.read() {
|
||||
s.disks.clone()
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("Failed to read state"));
|
||||
};
|
||||
|
||||
for disk in &disks {
|
||||
if disk.mount_point().to_string_lossy() == *mountpoint {
|
||||
let total = disk.total_space() as f64;
|
||||
let available = disk.available_space() as f64;
|
||||
if disk.mount_point == *mountpoint {
|
||||
let total = disk.total_bytes as f64;
|
||||
let available = disk.available_bytes as f64;
|
||||
let used = total - available;
|
||||
|
||||
|
||||
let used_gb = used / 1024.0 / 1024.0 / 1024.0;
|
||||
let total_gb = total / 1024.0 / 1024.0 / 1024.0;
|
||||
let free_gb = available / 1024.0 / 1024.0 / 1024.0;
|
||||
|
||||
let percentage = if total > 0.0 { (used / total) * 100.0 } else { 0.0 };
|
||||
|
||||
let percentage = if total > 0.0 {
|
||||
(used / total) * 100.0
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
let class = if percentage > 95.0 {
|
||||
"max"
|
||||
@@ -39,12 +47,15 @@ impl WaybarModule for DiskModule {
|
||||
("mount", TokenValue::String(mountpoint)),
|
||||
("used", TokenValue::Float(used_gb)),
|
||||
("total", TokenValue::Float(total_gb)),
|
||||
]
|
||||
],
|
||||
);
|
||||
|
||||
return Ok(WaybarOutput {
|
||||
text,
|
||||
tooltip: Some(format!("Used: {:.1}G\nTotal: {:.1}G\nFree: {:.1}G", used_gb, total_gb, free_gb)),
|
||||
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),
|
||||
});
|
||||
|
||||
+4
-13
@@ -2,25 +2,16 @@ use crate::config::Config;
|
||||
use crate::modules::WaybarModule;
|
||||
use crate::output::WaybarOutput;
|
||||
use crate::state::SharedState;
|
||||
use crate::utils::run_command;
|
||||
use anyhow::Result;
|
||||
use std::process::Command;
|
||||
|
||||
pub struct GameModule;
|
||||
|
||||
impl WaybarModule for GameModule {
|
||||
fn run(&self, config: &Config, _state: &SharedState, _args: &[&str]) -> Result<WaybarOutput> {
|
||||
let output = Command::new("hyprctl")
|
||||
.args(["getoption", "animations:enabled", "-j"])
|
||||
.output();
|
||||
|
||||
let mut is_gamemode = false;
|
||||
|
||||
if let Ok(out) = output {
|
||||
let stdout = String::from_utf8_lossy(&out.stdout);
|
||||
if stdout.contains("\"int\": 0") {
|
||||
is_gamemode = true;
|
||||
}
|
||||
}
|
||||
let is_gamemode = run_command("hyprctl", &["getoption", "animations:enabled", "-j"])
|
||||
.map(|stdout| stdout.contains("\"int\": 0"))
|
||||
.unwrap_or(false);
|
||||
|
||||
if is_gamemode {
|
||||
Ok(WaybarOutput {
|
||||
|
||||
+15
-4
@@ -2,7 +2,7 @@ use crate::config::Config;
|
||||
use crate::modules::WaybarModule;
|
||||
use crate::output::WaybarOutput;
|
||||
use crate::state::SharedState;
|
||||
use crate::utils::{format_template, TokenValue};
|
||||
use crate::utils::{TokenValue, format_template};
|
||||
use anyhow::Result;
|
||||
|
||||
pub struct GpuModule;
|
||||
@@ -21,7 +21,15 @@ impl WaybarModule for GpuModule {
|
||||
state_lock.gpu.model.clone(),
|
||||
)
|
||||
} else {
|
||||
(false, String::from("Unknown"), 0.0, 0.0, 0.0, 0.0, String::from("Unknown"))
|
||||
(
|
||||
false,
|
||||
String::from("Unknown"),
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
String::from("Unknown"),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -55,13 +63,16 @@ impl WaybarModule for GpuModule {
|
||||
("vram_used", TokenValue::Float(vram_used)),
|
||||
("vram_total", TokenValue::Float(vram_total)),
|
||||
("temp", TokenValue::Float(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)
|
||||
format!(
|
||||
"Model: {}\nUsage: {:.0}%\nVRAM: {:.1}/{:.1}GB\nTemp: {:.1}°C",
|
||||
model, usage, vram_used, vram_total, temp
|
||||
)
|
||||
};
|
||||
|
||||
Ok(WaybarOutput {
|
||||
|
||||
+150
-88
@@ -1,18 +1,27 @@
|
||||
use crate::state::SharedState;
|
||||
use sysinfo::{Components, System};
|
||||
use crate::state::{DiskInfo, SharedState};
|
||||
use sysinfo::{Components, Disks, System};
|
||||
|
||||
pub struct HardwareDaemon {
|
||||
sys: System,
|
||||
components: Components,
|
||||
gpu_vendor: Option<String>,
|
||||
gpu_poll_counter: u8,
|
||||
disk_poll_counter: u8,
|
||||
}
|
||||
|
||||
impl HardwareDaemon {
|
||||
pub fn new() -> Self {
|
||||
let mut sys = System::new_all();
|
||||
sys.refresh_all();
|
||||
let mut sys = System::new();
|
||||
sys.refresh_cpu_usage();
|
||||
sys.refresh_memory();
|
||||
let components = Components::new_with_refreshed_list();
|
||||
Self { sys, components, gpu_vendor: None }
|
||||
Self {
|
||||
sys,
|
||||
components,
|
||||
gpu_vendor: None,
|
||||
gpu_poll_counter: 0,
|
||||
disk_poll_counter: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll(&mut self, state: SharedState) {
|
||||
@@ -21,15 +30,25 @@ impl HardwareDaemon {
|
||||
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());
|
||||
let cpu_model = self
|
||||
.sys
|
||||
.cpus()
|
||||
.first()
|
||||
.map(|c| c.brand().to_string())
|
||||
.unwrap_or_else(|| "Unknown".to_string());
|
||||
|
||||
let mut cpu_temp = 0.0;
|
||||
for component in &self.components {
|
||||
let label = component.label().to_lowercase();
|
||||
if label.contains("tctl") || label.contains("cpu") || label.contains("package") || label.contains("temp1") {
|
||||
if let Some(temp) = component.temperature() {
|
||||
cpu_temp = temp as f64;
|
||||
if cpu_temp > 0.0 { break; }
|
||||
if (label.contains("tctl")
|
||||
|| label.contains("cpu")
|
||||
|| label.contains("package")
|
||||
|| label.contains("temp1"))
|
||||
&& let Some(temp) = component.temperature()
|
||||
{
|
||||
cpu_temp = temp as f64;
|
||||
if cpu_temp > 0.0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,20 +59,20 @@ impl HardwareDaemon {
|
||||
|
||||
let load_avg = System::load_average();
|
||||
let uptime = System::uptime();
|
||||
|
||||
|
||||
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 parts.len() >= 4
|
||||
&& 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;
|
||||
state_lock.cpu.temp = cpu_temp as f64;
|
||||
state_lock.cpu.temp = cpu_temp;
|
||||
state_lock.cpu.model = cpu_model;
|
||||
|
||||
state_lock.memory.total_gb = total_mem;
|
||||
@@ -65,100 +84,143 @@ impl HardwareDaemon {
|
||||
state_lock.sys.uptime = uptime;
|
||||
state_lock.sys.process_count = process_count;
|
||||
|
||||
self.poll_gpu(&mut state_lock.gpu);
|
||||
// Poll GPU every 5 seconds to avoid expensive nvidia-smi calls
|
||||
self.gpu_poll_counter = (self.gpu_poll_counter + 1) % 5;
|
||||
if self.gpu_poll_counter == 0 {
|
||||
self.poll_gpu(&mut state_lock.gpu);
|
||||
}
|
||||
|
||||
// Poll disks every 10 seconds
|
||||
self.disk_poll_counter = (self.disk_poll_counter + 1) % 10;
|
||||
if self.disk_poll_counter == 0 {
|
||||
state_lock.disks = Disks::new_with_refreshed_list()
|
||||
.iter()
|
||||
.map(|d| DiskInfo {
|
||||
mount_point: d.mount_point().to_string_lossy().into_owned(),
|
||||
filesystem: d.file_system().to_string_lossy().to_lowercase(),
|
||||
total_bytes: d.total_space(),
|
||||
available_bytes: d.available_space(),
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_gpu(&mut self, gpu: &mut crate::state::GpuState) {
|
||||
gpu.active = false;
|
||||
|
||||
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"])
|
||||
if (self.gpu_vendor.as_deref() == Some("NVIDIA") || self.gpu_vendor.is_none())
|
||||
&& 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;
|
||||
}
|
||||
}
|
||||
&& 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.gpu_vendor.as_deref() == Some("AMD") || self.gpu_vendor.as_deref() == Some("Intel") || self.gpu_vendor.is_none() {
|
||||
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 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;
|
||||
}
|
||||
|
||||
if (self.gpu_vendor.as_deref() == Some("AMD") || self.gpu_vendor.is_none())
|
||||
&& 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;
|
||||
}
|
||||
gpu.model = "AMD GPU".to_string();
|
||||
self.gpu_vendor = Some("AMD".to_string());
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
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);
|
||||
}
|
||||
if let Some(path) = freq_path
|
||||
&& let Ok(freq_str) = std::fs::read_to_string(&path)
|
||||
{
|
||||
gpu.active = true;
|
||||
gpu.vendor = "Intel".to_string();
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::config::Config;
|
||||
use crate::modules::WaybarModule;
|
||||
use crate::output::WaybarOutput;
|
||||
use crate::state::SharedState;
|
||||
use crate::utils::{format_template, TokenValue};
|
||||
use crate::utils::{TokenValue, format_template};
|
||||
use anyhow::Result;
|
||||
|
||||
pub struct MemoryModule;
|
||||
@@ -11,23 +11,24 @@ impl WaybarModule for MemoryModule {
|
||||
fn run(&self, config: &Config, state: &SharedState, _args: &[&str]) -> Result<WaybarOutput> {
|
||||
let (used_gb, total_gb) = {
|
||||
if let Ok(state_lock) = state.read() {
|
||||
(
|
||||
state_lock.memory.used_gb,
|
||||
state_lock.memory.total_gb,
|
||||
)
|
||||
(state_lock.memory.used_gb, state_lock.memory.total_gb)
|
||||
} else {
|
||||
(0.0, 0.0)
|
||||
}
|
||||
};
|
||||
|
||||
let ratio = if total_gb > 0.0 { (used_gb / total_gb) * 100.0 } else { 0.0 };
|
||||
let ratio = if total_gb > 0.0 {
|
||||
(used_gb / total_gb) * 100.0
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
let text = format_template(
|
||||
&config.memory.format,
|
||||
&[
|
||||
("used", TokenValue::Float(used_gb)),
|
||||
("total", TokenValue::Float(total_gb)),
|
||||
]
|
||||
],
|
||||
);
|
||||
|
||||
let class = if ratio > 95.0 {
|
||||
|
||||
+9
-10
@@ -1,16 +1,16 @@
|
||||
pub mod network;
|
||||
pub mod cpu;
|
||||
pub mod memory;
|
||||
pub mod hardware;
|
||||
pub mod disk;
|
||||
pub mod btrfs;
|
||||
pub mod audio;
|
||||
pub mod gpu;
|
||||
pub mod sys;
|
||||
pub mod bt;
|
||||
pub mod btrfs;
|
||||
pub mod buds;
|
||||
pub mod power;
|
||||
pub mod cpu;
|
||||
pub mod disk;
|
||||
pub mod game;
|
||||
pub mod gpu;
|
||||
pub mod hardware;
|
||||
pub mod memory;
|
||||
pub mod network;
|
||||
pub mod power;
|
||||
pub mod sys;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::output::WaybarOutput;
|
||||
@@ -20,4 +20,3 @@ use anyhow::Result;
|
||||
pub trait WaybarModule {
|
||||
fn run(&self, config: &Config, state: &SharedState, args: &[&str]) -> Result<WaybarOutput>;
|
||||
}
|
||||
|
||||
|
||||
+53
-34
@@ -2,7 +2,7 @@ use crate::config::Config;
|
||||
use crate::modules::WaybarModule;
|
||||
use crate::output::WaybarOutput;
|
||||
use crate::state::SharedState;
|
||||
use crate::utils::{format_template, TokenValue};
|
||||
use crate::utils::{TokenValue, format_template};
|
||||
use anyhow::Result;
|
||||
use std::fs;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
@@ -30,21 +30,20 @@ impl NetworkDaemon {
|
||||
|
||||
pub fn poll(&mut self, state: SharedState) {
|
||||
// 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 Some(ref iface) = self.cached_interface
|
||||
&& !std::path::Path::new(&format!("/sys/class/net/{}", iface)).exists()
|
||||
{
|
||||
self.cached_interface = None;
|
||||
self.cached_ip = None;
|
||||
}
|
||||
|
||||
// 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 self.cached_interface.is_none()
|
||||
&& let Ok(iface) = get_primary_interface()
|
||||
&& !iface.is_empty()
|
||||
{
|
||||
self.cached_ip = get_ip_address(&iface);
|
||||
self.cached_interface = Some(iface);
|
||||
}
|
||||
|
||||
if let Some(ref interface) = self.cached_interface {
|
||||
@@ -55,17 +54,26 @@ impl NetworkDaemon {
|
||||
|
||||
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;
|
||||
let time_diff = (time_now - self.last_time) as f64;
|
||||
let rx_mbps = (rx_bytes_now.saturating_sub(self.last_rx_bytes)) as f64
|
||||
/ time_diff
|
||||
/ 1024.0
|
||||
/ 1024.0;
|
||||
let tx_mbps = (tx_bytes_now.saturating_sub(self.last_tx_bytes)) as f64
|
||||
/ time_diff
|
||||
/ 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;
|
||||
state_lock.network.interface = interface.clone();
|
||||
state_lock.network.ip = self.cached_ip.clone().unwrap_or_default();
|
||||
}
|
||||
} else if let Ok(mut state_lock) = state.write() {
|
||||
// First poll: no speed data yet, but update interface/ip
|
||||
state_lock.network.interface = interface.clone();
|
||||
state_lock.network.ip = self.cached_ip.clone().unwrap_or_default();
|
||||
}
|
||||
|
||||
self.last_time = time_now;
|
||||
@@ -75,13 +83,32 @@ impl NetworkDaemon {
|
||||
// Read failed, might be down
|
||||
self.cached_interface = None;
|
||||
}
|
||||
} else if let Ok(mut state_lock) = state.write() {
|
||||
// No interface detected
|
||||
state_lock.network.interface.clear();
|
||||
state_lock.network.ip.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WaybarModule for NetworkModule {
|
||||
fn run(&self, config: &Config, state: &SharedState, _args: &[&str]) -> Result<WaybarOutput> {
|
||||
let interface = get_primary_interface()?;
|
||||
let (interface, ip, rx_mbps, tx_mbps) = if let Ok(s) = state.read() {
|
||||
(
|
||||
s.network.interface.clone(),
|
||||
s.network.ip.clone(),
|
||||
s.network.rx_mbps,
|
||||
s.network.tx_mbps,
|
||||
)
|
||||
} else {
|
||||
return Ok(WaybarOutput {
|
||||
text: "No connection".to_string(),
|
||||
tooltip: None,
|
||||
class: None,
|
||||
percentage: None,
|
||||
});
|
||||
};
|
||||
|
||||
if interface.is_empty() {
|
||||
return Ok(WaybarOutput {
|
||||
text: "No connection".to_string(),
|
||||
@@ -91,24 +118,16 @@ impl WaybarModule for NetworkModule {
|
||||
});
|
||||
}
|
||||
|
||||
let ip = get_ip_address(&interface).unwrap_or_else(|| String::from("No IP"));
|
||||
|
||||
let (rx_mbps, tx_mbps) = {
|
||||
if let Ok(state_lock) = state.read() {
|
||||
(state_lock.network.rx_mbps, state_lock.network.tx_mbps)
|
||||
} else {
|
||||
(0.0, 0.0)
|
||||
}
|
||||
};
|
||||
let ip_display = if ip.is_empty() { "No IP" } else { &ip };
|
||||
|
||||
let mut output_text = format_template(
|
||||
&config.network.format,
|
||||
&[
|
||||
("interface", TokenValue::String(&interface)),
|
||||
("ip", TokenValue::String(&ip)),
|
||||
("ip", TokenValue::String(ip_display)),
|
||||
("rx", TokenValue::Float(rx_mbps)),
|
||||
("tx", TokenValue::Float(tx_mbps)),
|
||||
]
|
||||
],
|
||||
);
|
||||
|
||||
if interface.starts_with("tun")
|
||||
@@ -116,12 +135,12 @@ impl WaybarModule for NetworkModule {
|
||||
|| interface.starts_with("ppp")
|
||||
|| interface.starts_with("pvpn")
|
||||
{
|
||||
output_text = format!(" {}", output_text);
|
||||
output_text = format!(" {}", output_text);
|
||||
}
|
||||
|
||||
Ok(WaybarOutput {
|
||||
text: output_text,
|
||||
tooltip: Some(format!("Interface: {}\nIP: {}", interface, ip)),
|
||||
tooltip: Some(format!("Interface: {}\nIP: {}", interface, ip_display)),
|
||||
class: Some(interface),
|
||||
percentage: None,
|
||||
})
|
||||
@@ -170,7 +189,7 @@ fn get_ip_address(interface: &str) -> Option<String> {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
for line in stdout.lines() {
|
||||
if line.trim().starts_with("inet ") {
|
||||
let parts: Vec<&str> = line.trim().split_whitespace().collect();
|
||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||
if parts.len() > 1 {
|
||||
let ip_cidr = parts[1];
|
||||
let ip = ip_cidr.split('/').next().unwrap_or(ip_cidr);
|
||||
|
||||
+11
-9
@@ -2,7 +2,7 @@ use crate::config::Config;
|
||||
use crate::modules::WaybarModule;
|
||||
use crate::output::WaybarOutput;
|
||||
use crate::state::SharedState;
|
||||
use crate::utils::{format_template, TokenValue};
|
||||
use crate::utils::{TokenValue, format_template};
|
||||
use anyhow::Result;
|
||||
use std::fs;
|
||||
|
||||
@@ -32,11 +32,11 @@ impl WaybarModule for PowerModule {
|
||||
let name = entry.file_name().to_string_lossy().to_string();
|
||||
if name.starts_with("AC") || name.starts_with("ADP") {
|
||||
let online_path = entry.path().join("online");
|
||||
if let Ok(online_str) = fs::read_to_string(online_path) {
|
||||
if online_str.trim() == "1" {
|
||||
ac_online = true;
|
||||
break;
|
||||
}
|
||||
if let Ok(online_str) = fs::read_to_string(online_path)
|
||||
&& online_str.trim() == "1"
|
||||
{
|
||||
ac_online = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,9 +60,11 @@ impl WaybarModule for PowerModule {
|
||||
}
|
||||
};
|
||||
|
||||
let capacity_str = fs::read_to_string(bat_path.join("capacity")).unwrap_or_else(|_| "0".to_string());
|
||||
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());
|
||||
let status_str =
|
||||
fs::read_to_string(bat_path.join("status")).unwrap_or_else(|_| "Unknown".to_string());
|
||||
let state = status_str.trim().to_lowercase();
|
||||
|
||||
let (icon, class, tooltip) = if state == "charging" || ac_online {
|
||||
@@ -95,7 +97,7 @@ impl WaybarModule for PowerModule {
|
||||
&[
|
||||
("percentage", TokenValue::Int(percentage as i64)),
|
||||
("icon", TokenValue::String(icon)),
|
||||
]
|
||||
],
|
||||
);
|
||||
|
||||
Ok(WaybarOutput {
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ use crate::config::Config;
|
||||
use crate::modules::WaybarModule;
|
||||
use crate::output::WaybarOutput;
|
||||
use crate::state::SharedState;
|
||||
use crate::utils::{format_template, TokenValue};
|
||||
use crate::utils::{TokenValue, format_template};
|
||||
use anyhow::Result;
|
||||
|
||||
pub struct SysModule;
|
||||
@@ -38,7 +38,7 @@ impl WaybarModule for SysModule {
|
||||
("load1", TokenValue::Float(load1)),
|
||||
("load5", TokenValue::Float(load5)),
|
||||
("load15", TokenValue::Float(load15)),
|
||||
]
|
||||
],
|
||||
);
|
||||
|
||||
Ok(WaybarOutput {
|
||||
|
||||
Reference in New Issue
Block a user