aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/config.rs4
-rw-r--r--src/main.rs26
-rw-r--r--src/niri.rs19
-rw-r--r--src/watcher.rs60
4 files changed, 103 insertions, 6 deletions
diff --git a/src/config.rs b/src/config.rs
index 91b4c2e1..82ff7fb2 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -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> {
diff --git a/src/main.rs b/src/main.rs
index 82f80dd3..e5e26bca 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -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);
diff --git a/src/niri.rs b/src/niri.rs
index ea061bc3..41c6a41e 100644
--- a/src/niri.rs
+++ b/src/niri.rs
@@ -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 {
diff --git a/src/watcher.rs b/src/watcher.rs
new file mode 100644
index 00000000..16e65b4d
--- /dev/null
+++ b/src/watcher.rs
@@ -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 }
+ }
+}