diff --git a/src/main.rs b/src/main.rs index fbf5763..742b5ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -62,5 +62,5 @@ fn run(cli: Cli) -> anyhow::Result { let config = Config::load(cli.config.as_deref()); tracing::info!("starting beer"); - wayland::run(config) + wayland::run(config, cli.config) } diff --git a/src/wayland.rs b/src/wayland.rs index ffdf8eb..71aed49 100644 --- a/src/wayland.rs +++ b/src/wayland.rs @@ -170,7 +170,7 @@ const DEFAULT_W: u32 = 800; const DEFAULT_H: u32 = 600; /// Run a single window until it is closed, returning the shell's exit code. -pub fn run(config: Config) -> anyhow::Result { +pub fn run(config: Config, config_path: Option) -> anyhow::Result { let conn = Connection::connect_to_env().context("connect to Wayland compositor")?; let (globals, event_queue) = registry_queue_init(&conn).context("initialize Wayland registry")?; @@ -254,6 +254,7 @@ pub fn run(config: Config) -> anyhow::Result { session: None, title: None, config, + config_path, bindings, font_size, fullscreen: false, @@ -286,6 +287,19 @@ pub fn run(config: Config) -> anyhow::Result { tracing::warn!("register blink timer: {err}"); } + // SIGUSR1 reloads the config in place. + match calloop::signals::Signals::new(&[calloop::signals::Signal::SIGUSR1]) { + Ok(signals) => { + let registered = event_loop + .handle() + .insert_source(signals, |_, _, app: &mut App| app.reload_config()); + if let Err(err) = registered { + tracing::warn!("register signal source: {err}"); + } + } + Err(err) => tracing::warn!("install SIGUSR1 handler: {err}"), + } + // Each iteration blocks until an event (PTY output, input, configure, frame // callback, blink) arrives, then presents at most one frame; bursts of PTY // output between frame callbacks coalesce into a single repaint. @@ -380,6 +394,8 @@ struct App { title: Option, /// The active user configuration. config: Config, + /// Path the config was loaded from, for SIGUSR1 live reload. + config_path: Option, /// Resolved key/text bindings. bindings: crate::bindings::Bindings, /// Current font size in pixels (changed by font-resize bindings). @@ -575,6 +591,37 @@ impl App { } } + /// Re-read the config file and apply it in place (SIGUSR1). + fn reload_config(&mut self) { + let new = Config::load(self.config_path.as_deref()); + self.bindings = + crate::bindings::Bindings::from_config(&new.key_bindings, &new.text_bindings); + self.renderer.set_padding(new.main.pad_x, new.main.pad_y); + if new.main.font != self.config.main.font || new.main.font_size != self.font_size { + match self.renderer.set_font(&new.main.font, new.main.font_size) { + Ok(()) => self.font_size = new.main.font_size, + Err(err) => tracing::warn!("reload font: {err:#}"), + } + } + if let Some(session) = self.session.as_mut() { + session + .term + .set_theme(crate::theme::Theme::from_config(&new.colors)); + let grid = session.term.grid_mut(); + grid.set_word_delimiters(new.main.word_delimiters.clone()); + grid.set_scrollback_cap(new.scrollback.lines); + if let Some(shape) = cursor_shape_from(new.cursor.style.as_deref()) { + grid.set_cursor_shape(shape); + } + grid.set_cursor_blink(new.cursor.blink); + } + self.config = new; + self.frames.clear(); + self.resize_grid(); + self.needs_draw = true; + tracing::info!("config reloaded"); + } + /// Re-rasterize the font at `new_size`, then re-derive the grid geometry. fn change_font_size(&mut self, new_size: u32) { let new_size = new_size.clamp(6, 200);