General Dev
This commit is contained in:
parent
255be18ca2
commit
e233750b24
5 changed files with 61 additions and 22 deletions
BIN
Pasted image.png
Normal file
BIN
Pasted image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
|
|
@ -10,7 +10,11 @@ pub fn spawn_ticker(sender: ComponentSender<App>) {
|
||||||
relm4::spawn(async move {
|
relm4::spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
sender.input(AppInput::ClockTick);
|
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;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,18 @@
|
||||||
use crate::{App, AppInput};
|
use crate::{App, AppInput};
|
||||||
use relm4::ComponentSender;
|
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");
|
pub 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");
|
pub 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");
|
pub 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_OFF: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/WiFi Connecting.svg");
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Stats {
|
pub struct Stats {
|
||||||
|
|
@ -22,7 +29,11 @@ struct CpuSnapshot {
|
||||||
idle: u64,
|
idle: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
static PREV_CPU: std::sync::OnceLock<Mutex<CpuSnapshot>> = std::sync::OnceLock::new();
|
static PREV_CPU: OnceLock<Mutex<CpuSnapshot>> = OnceLock::new();
|
||||||
|
static BAT_PATH: OnceLock<Option<PathBuf>> = OnceLock::new();
|
||||||
|
static WIFI_CACHE: LazyLock<Mutex<(String, &'static str)>> =
|
||||||
|
LazyLock::new(|| Mutex::new(("—".to_string(), WIFI_OFF)));
|
||||||
|
static WIFI_TICK: AtomicU8 = AtomicU8::new(0);
|
||||||
|
|
||||||
fn read_cpu() -> f32 {
|
fn read_cpu() -> f32 {
|
||||||
let text = fs::read_to_string("/proc/stat").unwrap_or_default();
|
let text = fs::read_to_string("/proc/stat").unwrap_or_default();
|
||||||
|
|
@ -65,15 +76,16 @@ fn read_ram() -> u64 {
|
||||||
total.saturating_sub(avail)
|
total.saturating_sub(avail)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bat_path() -> Option<PathBuf> {
|
fn bat_path() -> Option<&'static PathBuf> {
|
||||||
|
BAT_PATH
|
||||||
|
.get_or_init(|| {
|
||||||
fs::read_dir("/sys/class/power_supply")
|
fs::read_dir("/sys/class/power_supply")
|
||||||
.ok()?
|
.ok()?
|
||||||
.filter_map(|e| e.ok())
|
.filter_map(|e| e.ok())
|
||||||
.map(|e| e.path())
|
.map(|e| e.path())
|
||||||
.find(|p| {
|
.find(|p| p.file_name().map_or(false, |n| n.to_string_lossy().starts_with("BAT")))
|
||||||
p.file_name()
|
|
||||||
.map_or(false, |n| n.to_string_lossy().starts_with("BAT"))
|
|
||||||
})
|
})
|
||||||
|
.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_power() -> Option<f32> {
|
fn read_power() -> Option<f32> {
|
||||||
|
|
@ -161,7 +173,17 @@ pub async fn poll() -> Stats {
|
||||||
let mem = read_ram();
|
let mem = read_ram();
|
||||||
let power = read_power().map_or_else(|| " —W".into(), |w| format!("{w:4.1}W"));
|
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 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 {
|
Stats {
|
||||||
cpu: format!("{cpu:3.0}%"),
|
cpu: format!("{cpu:3.0}%"),
|
||||||
mem: if mem >= 1024 * 1024 {
|
mem: if mem >= 1024 * 1024 {
|
||||||
|
|
|
||||||
14
src/main.rs
14
src/main.rs
|
|
@ -25,6 +25,8 @@ pub struct App {
|
||||||
bat_lbl: gtk4::Label,
|
bat_lbl: gtk4::Label,
|
||||||
wifi_lbl: gtk4::Label,
|
wifi_lbl: gtk4::Label,
|
||||||
wifi_img: gtk4::Image,
|
wifi_img: gtk4::Image,
|
||||||
|
// Pre-loaded textures indexed by the WIFI_* constant pointer values.
|
||||||
|
wifi_textures: std::collections::HashMap<usize, gtk4::gdk::Texture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -53,7 +55,6 @@ impl SimpleComponent for App {
|
||||||
set_start_widget = >k::Box {
|
set_start_widget = >k::Box {
|
||||||
set_orientation: gtk::Orientation::Horizontal,
|
set_orientation: gtk::Orientation::Horizontal,
|
||||||
set_spacing: 0,
|
set_spacing: 0,
|
||||||
set_margin_start: 8,
|
|
||||||
|
|
||||||
#[name = "workspace_box"]
|
#[name = "workspace_box"]
|
||||||
gtk::Box {
|
gtk::Box {
|
||||||
|
|
@ -92,6 +93,12 @@ impl SimpleComponent for App {
|
||||||
wifi_lbl.set_max_width_chars(12);
|
wifi_lbl.set_max_width_chars(12);
|
||||||
let wifi_img = gtk4::Image::from_paintable(Some(&svg_texture(asset!("WiFi Connecting.svg"))));
|
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 {
|
let mut model = App {
|
||||||
workspaces: vec![],
|
workspaces: vec![],
|
||||||
active_ws: 1,
|
active_ws: 1,
|
||||||
|
|
@ -103,6 +110,7 @@ impl SimpleComponent for App {
|
||||||
bat_lbl: bat_lbl.clone(),
|
bat_lbl: bat_lbl.clone(),
|
||||||
wifi_lbl: wifi_lbl.clone(),
|
wifi_lbl: wifi_lbl.clone(),
|
||||||
wifi_img: wifi_img.clone(),
|
wifi_img: wifi_img.clone(),
|
||||||
|
wifi_textures,
|
||||||
};
|
};
|
||||||
let widgets = view_output!();
|
let widgets = view_output!();
|
||||||
model.workspace_box = widgets.workspace_box.clone();
|
model.workspace_box = widgets.workspace_box.clone();
|
||||||
|
|
@ -149,7 +157,9 @@ impl SimpleComponent for App {
|
||||||
self.pwr_lbl.set_label(&stats.power);
|
self.pwr_lbl.set_label(&stats.power);
|
||||||
self.bat_lbl.set_label(&stats.bat);
|
self.bat_lbl.set_label(&stats.bat);
|
||||||
self.wifi_lbl.set_label(&stats.wifi_ssid);
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11
src/theme.rs
11
src/theme.rs
|
|
@ -44,10 +44,13 @@ fn load_css() -> String {
|
||||||
};
|
};
|
||||||
|
|
||||||
format!(
|
format!(
|
||||||
"window.breadbar {{ background-color: {bg_rgba}; }}\
|
"* {{ font-family: 'JetBrainsMono Nerd Font Mono', monospace; font-size: 12px; }}\
|
||||||
.workspace-btn {{ background: {surface}; color: {fg}; border-radius: 4px;\
|
window.breadbar {{ background-color: {bg_rgba}; border-radius: 0; }}\
|
||||||
border: none; min-width: 24px; padding: 0 8px; }}\
|
.workspace-btn {{ background: transparent; color: {fg}; opacity: 0.45;\
|
||||||
.workspace-btn:hover, .workspace-btn.active {{ background: {accent}; }}\
|
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}; }}\
|
label {{ color: {fg}; }}\
|
||||||
window.breadbar-notification {{ background-color: alpha({bg_plain}, 0.95); }}\
|
window.breadbar-notification {{ background-color: alpha({bg_plain}, 0.95); }}\
|
||||||
.notification-card {{ background: {surface}; border-radius: 6px;\
|
.notification-card {{ background: {surface}; border-radius: 6px;\
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue