Add load-config-file --path to load a different config (#3395)

* ipc: allow load-config to relocate the path of the config

* doc: add info about alternative configuration paths and relocating

Co-authored-by: Ivan Molodetskikh <yalterz@gmail.com>

* Update docs/wiki/Integrating-niri.md

* Update niri-ipc/src/lib.rs

* Update src/ipc/server.rs

---------

Co-authored-by: Ivan Molodetskikh <yalterz@gmail.com>
This commit is contained in:
ジムワルド
2026-02-06 16:22:16 +00:00
committed by GitHub
parent 189917c933
commit 549148d277
6 changed files with 35 additions and 10 deletions
+3
View File
@@ -11,6 +11,9 @@ When this file is present, niri *will not* automatically create a config at `~/.
Keep in mind that we update the default config in new releases, so if you have a custom `/etc/niri/config.kdl`, you likely want to inspect and apply the relevant changes too. Keep in mind that we update the default config in new releases, so if you have a custom `/etc/niri/config.kdl`, you likely want to inspect and apply the relevant changes too.
The default configuration locations can be overridden with the `NIRI_CONFIG` environment variable.
You can also change the configuration path at runtime via the niri IPC or using the command `niri msg action load-config-file --path <path-to-config.kdl>`.
You can split the niri config file into multiple files using [`include`](./Configuration:-Include.md). You can split the niri config file into multiple files using [`include`](./Configuration:-Include.md).
### Xwayland ### Xwayland
+2 -2
View File
@@ -368,7 +368,7 @@ pub enum Action {
#[knuffel(skip)] #[knuffel(skip)]
UnsetWindowUrgent(u64), UnsetWindowUrgent(u64),
#[knuffel(skip)] #[knuffel(skip)]
LoadConfigFile, LoadConfigFile(#[knuffel(argument)] Option<String>),
#[knuffel(skip)] #[knuffel(skip)]
MruAdvance { MruAdvance {
direction: MruDirection, direction: MruDirection,
@@ -699,7 +699,7 @@ impl From<niri_ipc::Action> for Action {
niri_ipc::Action::ToggleWindowUrgent { id } => Self::ToggleWindowUrgent(id), niri_ipc::Action::ToggleWindowUrgent { id } => Self::ToggleWindowUrgent(id),
niri_ipc::Action::SetWindowUrgent { id } => Self::SetWindowUrgent(id), niri_ipc::Action::SetWindowUrgent { id } => Self::SetWindowUrgent(id),
niri_ipc::Action::UnsetWindowUrgent { id } => Self::UnsetWindowUrgent(id), niri_ipc::Action::UnsetWindowUrgent { id } => Self::UnsetWindowUrgent(id),
niri_ipc::Action::LoadConfigFile {} => Self::LoadConfigFile, niri_ipc::Action::LoadConfigFile { path } => Self::LoadConfigFile(path),
} }
} }
} }
+7 -1
View File
@@ -936,7 +936,13 @@ pub enum Action {
/// ///
/// Can be useful for scripts changing the config file, to avoid waiting the small duration for /// Can be useful for scripts changing the config file, to avoid waiting the small duration for
/// niri's config file watcher to notice the changes. /// niri's config file watcher to notice the changes.
LoadConfigFile {}, LoadConfigFile {
/// Path of a new config file to load.
///
/// If unset, reloads the current config file.
#[cfg_attr(feature = "clap", arg(long))]
path: Option<String>,
},
} }
/// Change in window or column size. /// Change in window or column size.
+2 -2
View File
@@ -2318,9 +2318,9 @@ impl State {
} }
self.niri.queue_redraw_all(); self.niri.queue_redraw_all();
} }
Action::LoadConfigFile => { Action::LoadConfigFile(path) => {
if let Some(watcher) = &self.niri.config_file_watcher { if let Some(watcher) = &self.niri.config_file_watcher {
watcher.load_config(); watcher.load_config(path);
} }
} }
Action::MruConfirm => { Action::MruConfirm => {
+9 -1
View File
@@ -463,7 +463,8 @@ async fn process(ctx: &ClientCtx, request: Request) -> Reply {
fn validate_action(action: &Action) -> Result<(), String> { fn validate_action(action: &Action) -> Result<(), String> {
if let Action::Screenshot { path, .. } if let Action::Screenshot { path, .. }
| Action::ScreenshotScreen { path, .. } | Action::ScreenshotScreen { path, .. }
| Action::ScreenshotWindow { path, .. } = action | Action::ScreenshotWindow { path, .. }
| Action::LoadConfigFile { path } = action
{ {
if let Some(path) = path { if let Some(path) = path {
// Relative paths are resolved against the niri compositor's working directory, which // Relative paths are resolved against the niri compositor's working directory, which
@@ -474,6 +475,13 @@ fn validate_action(action: &Action) -> Result<(), String> {
} }
} }
if let Action::LoadConfigFile { path: Some(path) } = action {
let p = Path::new(path);
if !p.is_file() {
return Err(format!("path does not point to a file: {path}"));
}
}
Ok(()) Ok(())
} }
+12 -4
View File
@@ -14,7 +14,7 @@ use crate::niri::State;
const POLLING_INTERVAL: Duration = Duration::from_millis(500); const POLLING_INTERVAL: Duration = Duration::from_millis(500);
pub struct Watcher { pub struct Watcher {
load_config: mpsc::Sender<()>, load_config: mpsc::Sender<Option<String>>,
} }
struct WatcherInner { struct WatcherInner {
@@ -67,7 +67,15 @@ impl Watcher {
loop { loop {
let mut should_load = match load_config_rx.recv_timeout(POLLING_INTERVAL) { let mut should_load = match load_config_rx.recv_timeout(POLLING_INTERVAL) {
Ok(()) => true, Ok(path) => {
if let Some(path) = path {
inner = WatcherInner::new(
ConfigPath::Explicit(PathBuf::from(path)),
Vec::new(),
);
}
true
}
Err(mpsc::RecvTimeoutError::Disconnected) => break, Err(mpsc::RecvTimeoutError::Disconnected) => break,
Err(mpsc::RecvTimeoutError::Timeout) => false, Err(mpsc::RecvTimeoutError::Timeout) => false,
}; };
@@ -105,8 +113,8 @@ impl Watcher {
Self { load_config } Self { load_config }
} }
pub fn load_config(&self) { pub fn load_config(&self, path: Option<String>) {
let _ = self.load_config.send(()); let _ = self.load_config.send(path);
} }
} }