Initial commit

This commit is contained in:
Breadway 2026-05-17 13:13:37 +08:00
parent 3100ee0591
commit 633467b6f2
13 changed files with 158 additions and 41 deletions

View file

@ -2,6 +2,21 @@ use crate::{App, AppInput};
use relm4::ComponentSender;
use std::{fs, path::PathBuf, sync::Mutex};
const WIFI_STRONG: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/WiFi Strong.svg");
const WIFI_MEDIUM: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/WiFi Medium.svg");
const WIFI_WEAK: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/WiFi Weak.svg");
const WIFI_OFF: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/WiFi Connecting.svg");
#[derive(Debug)]
pub struct Stats {
pub cpu: String,
pub mem: String,
pub power: String,
pub bat: String,
pub wifi_ssid: String,
pub wifi_icon: &'static str,
}
struct CpuSnapshot {
total: u64,
idle: u64,
@ -35,7 +50,7 @@ fn read_cpu() -> f32 {
(dtotal - didle) as f32 / dtotal as f32 * 100.0
}
fn read_ram() -> f32 {
fn read_ram() -> u64 {
let text = fs::read_to_string("/proc/meminfo").unwrap_or_default();
let mut total = 0u64;
let mut avail = 0u64;
@ -47,10 +62,7 @@ fn read_ram() -> f32 {
_ => {}
}
}
if total == 0 {
return 0.0;
}
(total - avail) as f32 / total as f32 * 100.0
total.saturating_sub(avail)
}
fn bat_path() -> Option<PathBuf> {
@ -92,29 +104,76 @@ fn read_battery() -> Option<u8> {
.ok()
}
async fn read_wifi() -> String {
let out = tokio::process::Command::new("iw")
async fn read_wifi() -> (String, &'static str) {
let dev_out = tokio::process::Command::new("iw")
.arg("dev")
.output()
.await
.ok();
let stdout = match out {
let dev_stdout = match dev_out {
Some(o) if o.status.success() => String::from_utf8_lossy(&o.stdout).into_owned(),
_ => return "".into(),
_ => return ("".into(), WIFI_OFF),
};
stdout
let iface = dev_stdout
.lines()
.find_map(|l| l.trim().strip_prefix("ssid ").map(str::to_string))
.unwrap_or_else(|| "".into())
.find_map(|l| l.trim().strip_prefix("Interface ").map(str::to_string));
let Some(iface) = iface else {
return ("".into(), WIFI_OFF);
};
let link_out = tokio::process::Command::new("iw")
.args(["dev", &iface, "link"])
.output()
.await
.ok();
let link_stdout = match link_out {
Some(o) if o.status.success() => String::from_utf8_lossy(&o.stdout).into_owned(),
_ => return ("".into(), WIFI_OFF),
};
let mut ssid = None;
let mut rssi: Option<i32> = None;
for line in link_stdout.lines() {
let t = line.trim();
if let Some(s) = t.strip_prefix("SSID: ") {
ssid = Some(s.to_string());
} else if let Some(r) = t.strip_prefix("signal: ") {
rssi = r.split_whitespace().next().and_then(|s| s.parse().ok());
}
}
let Some(ssid) = ssid else {
return ("".into(), WIFI_OFF);
};
let icon = match rssi {
Some(r) if r >= -55 => WIFI_STRONG,
Some(r) if r >= -70 => WIFI_MEDIUM,
_ => WIFI_WEAK,
};
(ssid, icon)
}
pub async fn poll() -> String {
pub async fn poll() -> Stats {
let cpu = read_cpu();
let ram = read_ram();
let power = read_power().map_or_else(|| "—W".into(), |w| format!("{w:.1}W"));
let bat = read_battery().map_or_else(|| "".into(), |p| format!("{p}%"));
let wifi = read_wifi().await;
format!("CPU {cpu:.0}% MEM {ram:.0}% {power} BAT {bat} {wifi}")
let mem = read_ram();
let power = read_power().map_or_else(|| " —W".into(), |w| format!("{w:4.1}W"));
let bat = read_battery().map_or_else(|| "".into(), |p| format!("{p:3}%"));
let (wifi_ssid, wifi_icon) = read_wifi().await;
Stats {
cpu: format!("{cpu:3.0}%"),
mem: if mem >= 1024 * 1024 {
format!("{:.1}G", mem as f32 / (1024.0 * 1024.0))
} else {
format!("{}M", mem / 1024)
},
power,
bat,
wifi_ssid,
wifi_icon,
}
}
pub fn spawn_poller(sender: ComponentSender<App>) {