atagen

monolith

public
Star0
Watch0

Commit 01e6a6d74d49

tarball

atagen <boss@atagen.co>2026-10-19 10:50 UTC

5 files changed+36-18
config: report startup warnings and summarise them on reload

Warnings from the first load were only logged, and Settings.warnings stayed empty until an IPC reload. A reload from a bind printed nothing at all unless the journal was open to its individual lines. get warnings now answers from Settings.warnings and every reload ends with one log line carrying the count.
Modified file: crates/ipc/src/lib.rs+1 additions
@@ -249,6 +249,7 @@ pub enum Request {
249249 pub enum Query {
250250 State,
251251 Version,
252+ Warnings,
252253 }
253254
254255 #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
Modified file: crates/monolith/src/commands.rs+18 additions−7 deletions
@@ -227,12 +227,11 @@ impl State {
227227 /// warnings for what had to be left out.
228228 pub fn reload(&mut self) -> Result<(), String> {
229229 let loaded = Config::load(self.settings.path.as_deref()).map_err(|e| e.to_string())?;
230- let mut warnings = loaded.warnings.clone();
231- let config = warn_config(loaded);
230+ let (config, binds, warnings) = adopt(loaded);
232231 let keyboard_changed = config.keyboard != self.settings.config.keyboard;
233232 let input_changed = config.input != self.settings.config.input;
234- self.settings.binds = Binds::new(&config);
235- warnings.extend(self.settings.binds.warnings.iter().cloned());
233+ let warned = warnings.len();
234+ self.settings.binds = binds;
236235 self.settings.warnings = warnings;
237236 self.wm.layout.gaps = crate::state::gaps(&config);
238237 self.wm.layout.autotile = config.general.autotile;
@@ -247,14 +246,26 @@ impl State {
247246 crate::drm::reconfigure_outputs(self);
248247 crate::nested::reconfigure(self);
249248 self.configure_idle();
250- tracing::info!("config reloaded");
249+ if warned == 0 {
250+ tracing::info!("config reloaded");
251+ } else {
252+ tracing::warn!(
253+ warned,
254+ "config reloaded with warnings, msg reload prints them"
255+ );
256+ }
251257 Ok(())
252258 }
253259 }
254260
255-pub fn warn_config(loaded: monolith_config::Loaded) -> Config {
261+/// Logs what loading left out and returns the config with the binds made
262+/// from it and every warning, which IPC reports.
263+pub fn adopt(loaded: monolith_config::Loaded) -> (Config, Binds, Vec<String>) {
256264 for w in &loaded.warnings {
257265 tracing::warn!("config: {w}");
258266 }
259- loaded.config
267+ let binds = Binds::new(&loaded.config);
268+ let mut warnings = loaded.warnings;
269+ warnings.extend(binds.warnings.iter().cloned());
270+ (loaded.config, binds, warnings)
260271 }
Modified file: crates/monolith/src/ipc.rs+3 additions
@@ -114,6 +114,9 @@ fn handle_line(state: &mut State, stream: &UnixStream, line: &[u8]) -> Option<En
114114 Err(e) => Reply::Error(e),
115115 },
116116 Request::Get(Query::State) => Reply::Ok(Some(Answer::State(snapshot(state)))),
117+ Request::Get(Query::Warnings) => {
118+ Reply::Ok(Some(Answer::Warnings(state.settings.warnings.clone())))
119+ }
117120 Request::Get(Query::Version) => {
118121 Reply::Ok(Some(Answer::Version(env!("CARGO_PKG_VERSION").into())))
119122 }
Modified file: crates/monolith/src/main.rs+2 additions
@@ -99,6 +99,7 @@ enum Backend {
9999 enum What {
100100 State,
101101 Version,
102+ Warnings,
102103 }
103104
104105 fn main() -> ExitCode {
@@ -123,6 +124,7 @@ fn main() -> ExitCode {
123124 return ipc::client(Request::Get(match what {
124125 What::State => Query::State,
125126 What::Version => Query::Version,
127+ What::Warnings => Query::Warnings,
126128 }));
127129 }
128130 Some(Cmd::Subscribe) => return ipc::client(Request::Subscribe {}),
Modified file: crates/monolith/src/state.rs+12 additions−11 deletions
@@ -122,15 +122,16 @@ impl State {
122122 ) -> Result<Self, Box<dyn std::error::Error>> {
123123 let dh = display.handle();
124124 let config_path = config_path.or_else(Config::path);
125- let config = match Config::load(config_path.as_deref()) {
126- Ok(loaded) => crate::commands::warn_config(loaded),
127- Err(e) => {
128- tracing::error!("config: {e}, using the built-in default");
129- Config::parse(monolith_config::DEFAULT)
130- .expect("the default config parses")
131- .config
132- }
133- };
125+ let loaded = Config::load(config_path.as_deref()).or_else(|e| {
126+ tracing::error!("config: {e}, using the built-in default");
127+ let mut default = Config::parse(monolith_config::DEFAULT)?;
128+ default
129+ .warnings
130+ .push(format!("{e}, using the built-in default"));
131+ Ok::<_, monolith_config::Error>(default)
132+ });
133+ let (config, binds, warnings) =
134+ crate::commands::adopt(loaded.expect("the default config parses"));
134135 let renderer = match gpu {
135136 Some(node) => VulkanRenderer::for_node(node)?,
136137 None => VulkanRenderer::new()?,
@@ -182,10 +183,10 @@ impl State {
182183 start: Instant::now(),
183184 },
184185 settings: Settings {
185- binds: Binds::new(&config),
186+ binds,
186187 config,
187188 path: config_path,
188- warnings: Vec::new(),
189+ warnings,
189190 },
190191 renderer,
191192 wm: Wm::new(layout, Animator::new(animation)),