atagen

headroom

public
Star0
Watch0
da174e47c3dfe446a07b3dd77fdfcd414277a44e
1
ssh://brewery@pls.lobotomise.me:33/atagen/headroom.git

Blame: crates/headroom-gui/src/main.rs

c19702ae09beatagen2026-05-27 15:49 UTC1
//! `headroom-gui` — native gpu-accelerated monitor for the headroom
c19702ae09beatagen2026-05-27 15:49 UTC2
//! daemon, built on [iced](https://iced.rs). additive and optional, with
c19702ae09beatagen2026-05-27 15:49 UTC3
//! full control parity with the ratatui tui (route/bypass/per-app/profile)
c19702ae09beatagen2026-05-27 15:49 UTC4
//! by mouse + keyboard; ships as its own nix package, separate from the
c19702ae09beatagen2026-05-27 15:49 UTC5
//! daemon. build from the nix dev shell (supplies iced's system libs).
c19702ae09beatagen2026-05-27 15:49 UTC6
c19702ae09beatagen2026-05-27 15:49 UTC7
mod app;
c19702ae09beatagen2026-05-27 15:49 UTC8
mod io;
c19702ae09beatagen2026-05-27 15:49 UTC9
c19702ae09beatagen2026-05-27 15:49 UTC10
use std::path::PathBuf;
c19702ae09beatagen2026-05-27 15:49 UTC11
c19702ae09beatagen2026-05-27 15:49 UTC12
use crate::app::HeadroomApp;
c19702ae09beatagen2026-05-27 15:49 UTC13
c19702ae09beatagen2026-05-27 15:49 UTC14
fn main() -> anyhow::Result<()> {
c19702ae09beatagen2026-05-27 15:49 UTC15
    init_tracing();
c19702ae09beatagen2026-05-27 15:49 UTC16
c19702ae09beatagen2026-05-27 15:49 UTC17
    let socket = parse_socket_arg();
c19702ae09beatagen2026-05-27 15:49 UTC18
c19702ae09beatagen2026-05-27 15:49 UTC19
    // connect + initial fetch before opening any window, so a dead daemon
c19702ae09beatagen2026-05-27 15:49 UTC20
    // fails fast. the reader thread lives inside the returned `Bootstrap`;
c19702ae09beatagen2026-05-27 15:49 UTC21
    // the iced `subscription` poll tick drains its channel.
c19702ae09beatagen2026-05-27 15:49 UTC22
    let bootstrap = io::start(socket)?;
c19702ae09beatagen2026-05-27 15:49 UTC23
c19702ae09beatagen2026-05-27 15:49 UTC24
    // iced's `BootFn` is `Fn` (callable repeatedly) but `Bootstrap` owns a
c19702ae09beatagen2026-05-27 15:49 UTC25
    // receiver and is single-use; stash it and `take()` on the first boot.
c19702ae09beatagen2026-05-27 15:49 UTC26
    let boot = std::sync::Mutex::new(Some(bootstrap));
c19702ae09beatagen2026-05-27 15:49 UTC27
c19702ae09beatagen2026-05-27 15:49 UTC28
    iced::application(
c19702ae09beatagen2026-05-27 15:49 UTC29
        move || HeadroomApp::new(boot.lock().unwrap().take().expect("boot runs once")),
c19702ae09beatagen2026-05-27 15:49 UTC30
        HeadroomApp::update,
c19702ae09beatagen2026-05-27 15:49 UTC31
        HeadroomApp::view,
c19702ae09beatagen2026-05-27 15:49 UTC32
    )
c19702ae09beatagen2026-05-27 15:49 UTC33
    .title(HeadroomApp::title)
c19702ae09beatagen2026-05-27 15:49 UTC34
    .subscription(HeadroomApp::subscription)
c19702ae09beatagen2026-05-27 15:49 UTC35
    .theme(|_state: &HeadroomApp| iced::Theme::Dark)
c19702ae09beatagen2026-05-27 15:49 UTC36
    .window_size(iced::Size::new(900.0, 680.0))
c19702ae09beatagen2026-05-27 15:49 UTC37
    .run()
c19702ae09beatagen2026-05-27 15:49 UTC38
    .map_err(|e| anyhow::anyhow!("iced: {e}"))?;
c19702ae09beatagen2026-05-27 15:49 UTC39
c19702ae09beatagen2026-05-27 15:49 UTC40
    Ok(())
c19702ae09beatagen2026-05-27 15:49 UTC41
}
c19702ae09beatagen2026-05-27 15:49 UTC42
c19702ae09beatagen2026-05-27 15:49 UTC43
/// minimal `--socket <path>` parsing — the only flag, so no clap.
c19702ae09beatagen2026-05-27 15:49 UTC44
fn parse_socket_arg() -> Option<PathBuf> {
c19702ae09beatagen2026-05-27 15:49 UTC45
    let mut args = std::env::args().skip(1);
c19702ae09beatagen2026-05-27 15:49 UTC46
    while let Some(arg) = args.next() {
c19702ae09beatagen2026-05-27 15:49 UTC47
        match arg.as_str() {
c19702ae09beatagen2026-05-27 15:49 UTC48
            "--socket" => return args.next().map(PathBuf::from),
c19702ae09beatagen2026-05-27 15:49 UTC49
            other => {
c19702ae09beatagen2026-05-27 15:49 UTC50
                if let Some(path) = other.strip_prefix("--socket=") {
c19702ae09beatagen2026-05-27 15:49 UTC51
                    return Some(PathBuf::from(path));
c19702ae09beatagen2026-05-27 15:49 UTC52
                }
c19702ae09beatagen2026-05-27 15:49 UTC53
            }
c19702ae09beatagen2026-05-27 15:49 UTC54
        }
c19702ae09beatagen2026-05-27 15:49 UTC55
    }
c19702ae09beatagen2026-05-27 15:49 UTC56
    None
c19702ae09beatagen2026-05-27 15:49 UTC57
}
c19702ae09beatagen2026-05-27 15:49 UTC58
c19702ae09beatagen2026-05-27 15:49 UTC59
fn init_tracing() {
c19702ae09beatagen2026-05-27 15:49 UTC60
    tracing_subscriber::fmt()
c19702ae09beatagen2026-05-27 15:49 UTC61
        .with_env_filter(
c19702ae09beatagen2026-05-27 15:49 UTC62
            tracing_subscriber::EnvFilter::try_from_default_env()
c19702ae09beatagen2026-05-27 15:49 UTC63
                .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("headroom_gui=info")),
c19702ae09beatagen2026-05-27 15:49 UTC64
        )
c19702ae09beatagen2026-05-27 15:49 UTC65
        .with_writer(std::io::stderr)
c19702ae09beatagen2026-05-27 15:49 UTC66
        .init();
c19702ae09beatagen2026-05-27 15:49 UTC67
}