From 4901b5d77d73db59f0c1545719ec6d1404aad979 Mon Sep 17 00:00:00 2001 From: cashmere Date: Sun, 16 Aug 2026 08:21:37 +0200 Subject: Run detached and reload config live --- src/main.rs | 100 +++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 69 insertions(+), 31 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index a4751d5..7c475c3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,28 +20,74 @@ mod tray; mod utils; mod wm_info_provider; +use std::env; use std::io::{self, ErrorKind, Read}; -use std::os::fd::AsRawFd; +use std::os::fd::{AsRawFd, RawFd}; use std::path::PathBuf; +use anyhow::Context; use clap::Parser; +use daemonize::Daemonize; use signal_hook::consts::*; use wayrs_client::{Connection, IoMode}; -use event_loop::EventLoop; +use config::{Config, ConfigWatcher}; +use event_loop::{Action, Callback, EventLoop}; use state::State; #[derive(Parser)] #[command(author, version, about, long_about = None)] struct Cli { - /// The path to a config file. - #[arg(short, long, value_name = "FILE")] + #[arg(short, long, value_name = "FILE", help = "The path to a config file")] config: Option, + #[arg(long, help = "Keep the bar attached to the current terminal")] + foreground: bool, +} + +fn status_callback(fd: RawFd) -> Callback { + Box::new(move |ctx| { + if ctx.state.status_cmd_fd() != Some(fd) { + return Ok(Action::Unregister); + } + match ctx + .state + .shared_state + .status_cmd + .as_mut() + .unwrap() + .receive_blocks() + { + Ok(None) => Ok(Action::Keep), + Ok(Some(blocks)) => { + ctx.state.set_blocks(ctx.conn, blocks); + Ok(Action::Keep) + } + Err(e) => { + let _ = ctx + .state + .shared_state + .status_cmd + .take() + .unwrap() + .child + .kill(); + ctx.state.set_error(ctx.conn, "status", e); + Ok(Action::Unregister) + } + } + }) } fn main() -> anyhow::Result<()> { let args = Cli::parse(); - + let config_path = Config::path(args.config.as_deref()); + if !args.foreground { + let working_directory = env::current_dir().context("failed to read working directory")?; + Daemonize::new() + .working_directory(working_directory) + .start() + .context("failed to start in the background")?; + } let (mut sig_read, sig_write) = io::pipe()?; signal_hook::low_level::pipe::register(SIGUSR1, sig_write)?; @@ -49,7 +95,7 @@ fn main() -> anyhow::Result<()> { conn.blocking_roundtrip()?; let mut el = EventLoop::new(); - let mut state = State::new(&mut conn, &mut el, args.config.as_deref()); + let mut state = State::new(&mut conn, &mut el, config_path.as_deref()); conn.flush(IoMode::Blocking)?; el.add_on_idle(|ctx| { @@ -73,31 +119,23 @@ fn main() -> anyhow::Result<()> { }); if let Some(fd) = state.status_cmd_fd() { - el.register_with_fd(fd, |ctx| { - match ctx - .state - .shared_state - .status_cmd - .as_mut() - .unwrap() - .receive_blocks() - { - Ok(None) => Ok(event_loop::Action::Keep), - Ok(Some(blocks)) => { - ctx.state.set_blocks(ctx.conn, blocks); - Ok(event_loop::Action::Keep) - } - Err(e) => { - let _ = ctx - .state - .shared_state - .status_cmd - .take() - .unwrap() - .child - .kill(); - ctx.state.set_error(ctx.conn, "status", e); - Ok(event_loop::Action::Unregister) + el.register_with_fd(fd, status_callback(fd)); + } + + if let Some(config_path) = config_path { + let mut watcher = ConfigWatcher::new(&config_path) + .with_context(|| format!("failed to watch {}", config_path.display()))?; + let watcher_fd = watcher.fd(); + el.register_with_fd(watcher_fd, move |ctx| { + if !watcher.changed()? { + return Ok(Action::Keep); + } + match ctx.state.reload_config(ctx.conn, &config_path) { + Ok(Some(fd)) => Ok(Action::KeepAndRegister(fd, status_callback(fd))), + Ok(None) => Ok(Action::Keep), + Err(error) => { + ctx.state.set_error(ctx.conn, "config", error); + Ok(Action::Keep) } } }); -- cgit v1.3.1