summaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
authorcashmere <cashmere@autistici.org>2026-08-16 08:21:37 +0200
committercashmere <cashmere@autistici.org>2026-08-16 08:21:37 +0200
commit4901b5d77d73db59f0c1545719ec6d1404aad979 (patch)
tree0b3233e82387ffe7a9cd5c0934971a665e0575a9 /src/main.rs
parent91b84d1f1bfbc5de2eb140a48185a5252b1b592e (diff)
downloadi3bar-mango-main.tar.gz
Run detached and reload config liveHEADmain
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs100
1 files changed, 69 insertions, 31 deletions
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<PathBuf>,
+ #[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)
}
}
});