diff --git a/Pasted image.png b/Pasted image.png new file mode 100644 index 0000000..0bff4e5 Binary files /dev/null and b/Pasted image.png differ diff --git a/src/bar/clock.rs b/src/bar/clock.rs index 78b0c22..aaa5c65 100644 --- a/src/bar/clock.rs +++ b/src/bar/clock.rs @@ -10,7 +10,11 @@ pub fn spawn_ticker(sender: ComponentSender) { relm4::spawn(async move { loop { sender.input(AppInput::ClockTick); - tokio::time::sleep(std::time::Duration::from_secs(1)).await; + // Sleep until the top of the next minute — display is HH:MM only. + let secs = gtk4::glib::DateTime::now_local() + .map_or(0, |dt| dt.second()); + let wait = (60 - secs.rem_euclid(60)) as u64; + tokio::time::sleep(std::time::Duration::from_secs(wait.max(1))).await; } }); } diff --git a/src/bar/stats.rs b/src/bar/stats.rs index 18f12b8..7a73c85 100644 --- a/src/bar/stats.rs +++ b/src/bar/stats.rs @@ -1,11 +1,18 @@ use crate::{App, AppInput}; use relm4::ComponentSender; -use std::{fs, path::PathBuf, sync::Mutex}; +use std::{ + fs, + path::PathBuf, + sync::{ + atomic::{AtomicU8, Ordering}, + LazyLock, Mutex, OnceLock, + }, +}; -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"); +pub const WIFI_STRONG: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/WiFi Strong.svg"); +pub const WIFI_MEDIUM: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/WiFi Medium.svg"); +pub const WIFI_WEAK: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/WiFi Weak.svg"); +pub const WIFI_OFF: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/WiFi Connecting.svg"); #[derive(Debug)] pub struct Stats { @@ -22,7 +29,11 @@ struct CpuSnapshot { idle: u64, } -static PREV_CPU: std::sync::OnceLock> = std::sync::OnceLock::new(); +static PREV_CPU: OnceLock> = OnceLock::new(); +static BAT_PATH: OnceLock> = OnceLock::new(); +static WIFI_CACHE: LazyLock> = + LazyLock::new(|| Mutex::new(("—".to_string(), WIFI_OFF))); +static WIFI_TICK: AtomicU8 = AtomicU8::new(0); fn read_cpu() -> f32 { let text = fs::read_to_string("/proc/stat").unwrap_or_default(); @@ -65,15 +76,16 @@ fn read_ram() -> u64 { total.saturating_sub(avail) } -fn bat_path() -> Option { - fs::read_dir("/sys/class/power_supply") - .ok()? - .filter_map(|e| e.ok()) - .map(|e| e.path()) - .find(|p| { - p.file_name() - .map_or(false, |n| n.to_string_lossy().starts_with("BAT")) +fn bat_path() -> Option<&'static PathBuf> { + BAT_PATH + .get_or_init(|| { + fs::read_dir("/sys/class/power_supply") + .ok()? + .filter_map(|e| e.ok()) + .map(|e| e.path()) + .find(|p| p.file_name().map_or(false, |n| n.to_string_lossy().starts_with("BAT"))) }) + .as_ref() } fn read_power() -> Option { @@ -161,7 +173,17 @@ pub async fn poll() -> Stats { 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; + // Refresh WiFi every 8 cycles (~16 s); cache the result in between. + let (wifi_ssid, wifi_icon) = { + let tick = WIFI_TICK.fetch_add(1, Ordering::Relaxed); + if tick % 8 == 0 { + let fresh = read_wifi().await; + *WIFI_CACHE.lock().unwrap() = fresh.clone(); + fresh + } else { + WIFI_CACHE.lock().unwrap().clone() + } + }; Stats { cpu: format!("{cpu:3.0}%"), mem: if mem >= 1024 * 1024 { diff --git a/src/main.rs b/src/main.rs index d1b5d6d..9e4827a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,8 @@ pub struct App { bat_lbl: gtk4::Label, wifi_lbl: gtk4::Label, wifi_img: gtk4::Image, + // Pre-loaded textures indexed by the WIFI_* constant pointer values. + wifi_textures: std::collections::HashMap, } #[derive(Debug)] @@ -53,7 +55,6 @@ impl SimpleComponent for App { set_start_widget = >k::Box { set_orientation: gtk::Orientation::Horizontal, set_spacing: 0, - set_margin_start: 8, #[name = "workspace_box"] gtk::Box { @@ -92,6 +93,12 @@ impl SimpleComponent for App { wifi_lbl.set_max_width_chars(12); let wifi_img = gtk4::Image::from_paintable(Some(&svg_texture(asset!("WiFi Connecting.svg")))); + use bar::stats::{WIFI_OFF, WIFI_STRONG, WIFI_MEDIUM, WIFI_WEAK}; + let wifi_textures = [WIFI_STRONG, WIFI_MEDIUM, WIFI_WEAK, WIFI_OFF] + .into_iter() + .map(|p| (p.as_ptr() as usize, svg_texture(p))) + .collect(); + let mut model = App { workspaces: vec![], active_ws: 1, @@ -103,6 +110,7 @@ impl SimpleComponent for App { bat_lbl: bat_lbl.clone(), wifi_lbl: wifi_lbl.clone(), wifi_img: wifi_img.clone(), + wifi_textures, }; let widgets = view_output!(); model.workspace_box = widgets.workspace_box.clone(); @@ -149,7 +157,9 @@ impl SimpleComponent for App { self.pwr_lbl.set_label(&stats.power); self.bat_lbl.set_label(&stats.bat); self.wifi_lbl.set_label(&stats.wifi_ssid); - self.wifi_img.set_paintable(Some(&svg_texture(stats.wifi_icon))); + if let Some(tex) = self.wifi_textures.get(&(stats.wifi_icon.as_ptr() as usize)) { + self.wifi_img.set_paintable(Some(tex)); + } } } } diff --git a/src/theme.rs b/src/theme.rs index 5dcfb1d..e5db59c 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -44,10 +44,13 @@ fn load_css() -> String { }; format!( - "window.breadbar {{ background-color: {bg_rgba}; }}\ - .workspace-btn {{ background: {surface}; color: {fg}; border-radius: 4px;\ - border: none; min-width: 24px; padding: 0 8px; }}\ - .workspace-btn:hover, .workspace-btn.active {{ background: {accent}; }}\ + "* {{ font-family: 'JetBrainsMono Nerd Font Mono', monospace; font-size: 12px; }}\ + window.breadbar {{ background-color: {bg_rgba}; border-radius: 0; }}\ + .workspace-btn {{ background: transparent; color: {fg}; opacity: 0.45;\ + border-radius: 0 0 8px 8px; border: none; outline: none; box-shadow: none;\ + min-width: 24px; padding: 2px 8px; }}\ + .workspace-btn:hover {{ opacity: 0.8; }}\ + .workspace-btn.active {{ background: {accent}; opacity: 1; }}\ label {{ color: {fg}; }}\ window.breadbar-notification {{ background-color: alpha({bg_plain}, 0.95); }}\ .notification-card {{ background: {surface}; border-radius: 6px;\