105 lines
3.1 KiB
Rust
105 lines
3.1 KiB
Rust
// Wi-Fi widget backend. Polls NetworkManager every second using `nmcli`.
|
|
use super::common::run_cmd;
|
|
use crate::TopBar;
|
|
use slint::{ComponentHandle, SharedString, Timer, TimerMode};
|
|
use std::process::Command;
|
|
use std::time::Duration;
|
|
|
|
/// Run a shell command and capture stdout as a string
|
|
fn run_cmd_output(cmd: &str, args: &[&str]) -> Option<String> {
|
|
let out = Command::new(cmd).args(args).output().ok()?;
|
|
if !out.status.success() {
|
|
return None;
|
|
}
|
|
Some(String::from_utf8_lossy(&out.stdout).trim().to_string())
|
|
}
|
|
|
|
/// Return (connected: bool, ssid: String, signal: u8)
|
|
fn read_wifi_status() -> Option<(bool, String, u8)> {
|
|
// Fields: ACTIVE:SSID:SIGNAL
|
|
let out = run_cmd_output("nmcli", &["-t", "-f", "ACTIVE,SSID,SIGNAL", "dev", "wifi"])?;
|
|
for line in out.lines() {
|
|
let mut parts = line.split(':');
|
|
let active = parts.next()?.trim();
|
|
let ssid = parts.next().unwrap_or("").trim().to_string();
|
|
let signal = parts.next().unwrap_or("0").trim().parse().unwrap_or(0);
|
|
|
|
if active == "yes" {
|
|
return Some((true, ssid, signal));
|
|
}
|
|
}
|
|
Some((false, String::new(), 0))
|
|
}
|
|
|
|
/// True if Wi-Fi hardware is enabled
|
|
fn wifi_enabled() -> bool {
|
|
// nmcli -t -f WIFI g → "enabled" or "disabled"
|
|
if let Some(out) = run_cmd_output("nmcli", &["-t", "-f", "WIFI", "g"]) {
|
|
return out.trim() == "enabled";
|
|
}
|
|
false
|
|
}
|
|
|
|
/// Choose icon based on Wi-Fi signal
|
|
fn wifi_icon(signal: u8, connected: bool, enabled: bool) -> SharedString {
|
|
let icon = if !enabled {
|
|
"" // wifi off
|
|
} else if !connected {
|
|
"" // wifi on, no connection
|
|
} else {
|
|
match signal {
|
|
0..=20 => "",
|
|
21..=40 => "",
|
|
41..=60 => "",
|
|
61..=100 => "",
|
|
_ => "", // error/unexpected
|
|
}
|
|
};
|
|
SharedString::from(icon)
|
|
}
|
|
|
|
/// Tooltip shown on hover
|
|
fn wifi_tooltip(connected: bool, ssid: &str, signal: u8, enabled: bool) -> String {
|
|
if !enabled {
|
|
"Wi-Fi: disabled".into()
|
|
} else if !connected {
|
|
"Wi-Fi: no internet".into()
|
|
} else {
|
|
format!("Wi-Fi: {ssid} ({signal}%)")
|
|
}
|
|
}
|
|
|
|
/// Periodic Wi-Fi updater
|
|
fn start_wifi_updater(ui: &TopBar) {
|
|
let weak = ui.as_weak();
|
|
|
|
let timer = Box::leak(Box::new(Timer::default()));
|
|
|
|
// Update every second (connection quality can change quickly)
|
|
timer.start(TimerMode::Repeated, Duration::from_secs(1), move || {
|
|
if let Some(ui) = weak.upgrade() {
|
|
let enabled = wifi_enabled();
|
|
let (connected, ssid, signal) = read_wifi_status().unwrap_or((false, String::new(), 0));
|
|
|
|
let icon = wifi_icon(signal, connected, enabled);
|
|
let tooltip = wifi_tooltip(connected, &ssid, signal, enabled);
|
|
|
|
ui.set_wifi_icon(icon);
|
|
ui.set_wifi_tooltip(tooltip.into());
|
|
}
|
|
});
|
|
}
|
|
|
|
/// Install Wi-Fi click action + updater
|
|
pub fn install(ui: &TopBar) {
|
|
let weak = ui.as_weak();
|
|
|
|
ui.on_show_wifi(move || {
|
|
if weak.upgrade().is_some() {
|
|
run_cmd("xclock"); // Fix later
|
|
}
|
|
});
|
|
|
|
start_wifi_updater(ui);
|
|
}
|