mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-22 02:01:55 +07:00
Add initial config hot reloading
This commit is contained in:
+2
-2
@@ -237,7 +237,7 @@ impl Default for DebugConfig {
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn load(path: Option<PathBuf>) -> miette::Result<Self> {
|
||||
pub fn load(path: Option<PathBuf>) -> miette::Result<(Self, PathBuf)> {
|
||||
let path = if let Some(path) = path {
|
||||
path
|
||||
} else {
|
||||
@@ -255,7 +255,7 @@ impl Config {
|
||||
|
||||
let config = Self::parse("config.kdl", &contents).context("error parsing")?;
|
||||
debug!("loaded config from {path:?}");
|
||||
Ok(config)
|
||||
Ok((config, path))
|
||||
}
|
||||
|
||||
pub fn parse(filename: &str, text: &str) -> Result<Self, knuffel::Error> {
|
||||
|
||||
+22
-4
@@ -12,6 +12,7 @@ mod layout;
|
||||
mod niri;
|
||||
mod pw_utils;
|
||||
mod utils;
|
||||
mod watcher;
|
||||
|
||||
use std::ffi::OsString;
|
||||
use std::path::PathBuf;
|
||||
@@ -22,10 +23,11 @@ use config::Config;
|
||||
use miette::Context;
|
||||
use niri::{Niri, State};
|
||||
use portable_atomic::Ordering;
|
||||
use smithay::reexports::calloop::EventLoop;
|
||||
use smithay::reexports::calloop::{self, EventLoop};
|
||||
use smithay::reexports::wayland_server::Display;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
use utils::spawn;
|
||||
use watcher::Watcher;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
@@ -52,11 +54,11 @@ fn main() {
|
||||
|
||||
let _client = tracy_client::Client::start();
|
||||
|
||||
let mut config = match Config::load(cli.config).context("error loading config") {
|
||||
Ok(config) => config,
|
||||
let (mut config, path) = match Config::load(cli.config).context("error loading config") {
|
||||
Ok((config, path)) => (config, Some(path)),
|
||||
Err(err) => {
|
||||
warn!("{err:?}");
|
||||
Config::default()
|
||||
(Config::default(), None)
|
||||
}
|
||||
};
|
||||
animation::ANIMATION_SLOWDOWN.store(config.debug.animation_slowdown, Ordering::Relaxed);
|
||||
@@ -71,6 +73,22 @@ fn main() {
|
||||
display,
|
||||
);
|
||||
|
||||
// Set up config file watcher.
|
||||
let _watcher = if let Some(path) = path {
|
||||
let (tx, rx) = calloop::channel::sync_channel(1);
|
||||
let watcher = Watcher::new(path.clone(), tx);
|
||||
event_loop
|
||||
.handle()
|
||||
.insert_source(rx, move |event, _, state| match event {
|
||||
calloop::channel::Event::Msg(()) => state.reload_config(path.clone()),
|
||||
calloop::channel::Event::Closed => (),
|
||||
})
|
||||
.unwrap();
|
||||
Some(watcher)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Spawn commands from cli and auto-start.
|
||||
if let Some((command, args)) = cli.command.split_first() {
|
||||
spawn(command, args);
|
||||
|
||||
+19
@@ -197,6 +197,25 @@ impl State {
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reload_config(&mut self, path: PathBuf) {
|
||||
let _span = tracy_client::span!("State::reload_config");
|
||||
|
||||
let config = match Config::load(Some(path)) {
|
||||
Ok((config, _)) => config,
|
||||
Err(err) => {
|
||||
warn!("{:?}", err.context("error loading config"));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
*self.niri.config.borrow_mut() = config;
|
||||
self.niri.queue_redraw_all();
|
||||
// FIXME: apply output scale and whatnot.
|
||||
// FIXME: apply libinput device settings.
|
||||
// FIXME: apply xkb settings.
|
||||
// FIXME: apply xdg decoration settings.
|
||||
}
|
||||
}
|
||||
|
||||
impl Niri {
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
//! File modification watcher.
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use smithay::reexports::calloop::channel::SyncSender;
|
||||
|
||||
pub struct Watcher {
|
||||
should_stop: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl Drop for Watcher {
|
||||
fn drop(&mut self) {
|
||||
self.should_stop.store(true, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
impl Watcher {
|
||||
pub fn new(path: PathBuf, changed: SyncSender<()>) -> Self {
|
||||
let should_stop = Arc::new(AtomicBool::new(false));
|
||||
|
||||
{
|
||||
let should_stop = should_stop.clone();
|
||||
thread::Builder::new()
|
||||
.name(format!("Filesystem Watcher for {}", path.to_string_lossy()))
|
||||
.spawn(move || {
|
||||
let mut last_mtime = path.metadata().and_then(|meta| meta.modified()).ok();
|
||||
|
||||
loop {
|
||||
thread::sleep(Duration::from_millis(500));
|
||||
|
||||
if should_stop.load(Ordering::SeqCst) {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Ok(mtime) = path.metadata().and_then(|meta| meta.modified()) {
|
||||
if last_mtime != Some(mtime) {
|
||||
trace!("file changed: {}", path.to_string_lossy());
|
||||
|
||||
if let Err(err) = changed.send(()) {
|
||||
warn!("error sending change notification: {err:?}");
|
||||
break;
|
||||
}
|
||||
|
||||
last_mtime = Some(mtime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!("exiting watcher thread for {}", path.to_string_lossy());
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
Self { should_stop }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user