| 1 | //! `headroom-gui` — native gpu-accelerated monitor for the headroom |
| 2 | //! daemon, built on [iced](https://iced.rs). additive and optional, with |
| 3 | //! full control parity with the ratatui tui (route/bypass/per-app/profile) |
| 4 | //! by mouse + keyboard; ships as its own nix package, separate from the |
| 5 | //! daemon. build from the nix dev shell (supplies iced's system libs). |
| 6 | |
| 7 | mod app; |
| 8 | mod io; |
| 9 | |
| 10 | use std::path::PathBuf; |
| 11 | |
| 12 | use crate::app::HeadroomApp; |
| 13 | |
| 14 | fn main() -> anyhow::Result<()> { |
| 15 | init_tracing(); |
| 16 | |
| 17 | let socket = parse_socket_arg(); |
| 18 | |
| 19 | // connect + initial fetch before opening any window, so a dead daemon |
| 20 | // fails fast. the reader thread lives inside the returned `Bootstrap`; |
| 21 | // the iced `subscription` poll tick drains its channel. |
| 22 | let bootstrap = io::start(socket)?; |
| 23 | |
| 24 | // iced's `BootFn` is `Fn` (callable repeatedly) but `Bootstrap` owns a |
| 25 | // receiver and is single-use; stash it and `take()` on the first boot. |
| 26 | let boot = std::sync::Mutex::new(Some(bootstrap)); |
| 27 | |
| 28 | iced::application( |
| 29 | move || HeadroomApp::new(boot.lock().unwrap().take().expect("boot runs once")), |
| 30 | HeadroomApp::update, |
| 31 | HeadroomApp::view, |
| 32 | ) |
| 33 | .title(HeadroomApp::title) |
| 34 | .subscription(HeadroomApp::subscription) |
| 35 | .theme(|_state: &HeadroomApp| iced::Theme::Dark) |
| 36 | .window_size(iced::Size::new(900.0, 680.0)) |
| 37 | .run() |
| 38 | .map_err(|e| anyhow::anyhow!("iced: {e}"))?; |
| 39 | |
| 40 | Ok(()) |
| 41 | } |
| 42 | |
| 43 | /// minimal `--socket <path>` parsing — the only flag, so no clap. |
| 44 | fn parse_socket_arg() -> Option<PathBuf> { |
| 45 | let mut args = std::env::args().skip(1); |
| 46 | while let Some(arg) = args.next() { |
| 47 | match arg.as_str() { |
| 48 | "--socket" => return args.next().map(PathBuf::from), |
| 49 | other => { |
| 50 | if let Some(path) = other.strip_prefix("--socket=") { |
| 51 | return Some(PathBuf::from(path)); |
| 52 | } |
| 53 | } |
| 54 | } |
| 55 | } |
| 56 | None |
| 57 | } |
| 58 | |
| 59 | fn init_tracing() { |
| 60 | tracing_subscriber::fmt() |
| 61 | .with_env_filter( |
| 62 | tracing_subscriber::EnvFilter::try_from_default_env() |
| 63 | .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("headroom_gui=info")), |
| 64 | ) |
| 65 | .with_writer(std::io::stderr) |
| 66 | .init(); |
| 67 | } |