mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-24 02:01:18 +07:00
Add the LoadConfigFile action (#2163)
* Add the `LoadConfigFile` action * fixes --------- Co-authored-by: Ivan Molodetskikh <yalterz@gmail.com>
This commit is contained in:
@@ -1924,6 +1924,8 @@ pub enum Action {
|
|||||||
SetWindowUrgent(u64),
|
SetWindowUrgent(u64),
|
||||||
#[knuffel(skip)]
|
#[knuffel(skip)]
|
||||||
UnsetWindowUrgent(u64),
|
UnsetWindowUrgent(u64),
|
||||||
|
#[knuffel(skip)]
|
||||||
|
LoadConfigFile,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<niri_ipc::Action> for Action {
|
impl From<niri_ipc::Action> for Action {
|
||||||
@@ -2199,6 +2201,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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -840,6 +840,11 @@ pub enum Action {
|
|||||||
#[cfg_attr(feature = "clap", arg(long))]
|
#[cfg_attr(feature = "clap", arg(long))]
|
||||||
id: u64,
|
id: u64,
|
||||||
},
|
},
|
||||||
|
/// Reload the config file.
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
LoadConfigFile {},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change in window or column size.
|
/// Change in window or column size.
|
||||||
|
|||||||
@@ -2113,6 +2113,11 @@ impl State {
|
|||||||
}
|
}
|
||||||
self.niri.queue_redraw_all();
|
self.niri.queue_redraw_all();
|
||||||
}
|
}
|
||||||
|
Action::LoadConfigFile => {
|
||||||
|
if let Some(watcher) = &self.niri.config_file_watcher {
|
||||||
|
watcher.load_config();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-23
@@ -23,8 +23,7 @@ use niri::utils::spawning::{
|
|||||||
spawn, store_and_increase_nofile_rlimit, CHILD_DISPLAY, CHILD_ENV, REMOVE_ENV_RUST_BACKTRACE,
|
spawn, store_and_increase_nofile_rlimit, CHILD_DISPLAY, CHILD_ENV, REMOVE_ENV_RUST_BACKTRACE,
|
||||||
REMOVE_ENV_RUST_LIB_BACKTRACE,
|
REMOVE_ENV_RUST_LIB_BACKTRACE,
|
||||||
};
|
};
|
||||||
use niri::utils::watcher::Watcher;
|
use niri::utils::{cause_panic, version, watcher, xwayland, IS_SYSTEMD_SERVICE};
|
||||||
use niri::utils::{cause_panic, version, xwayland, IS_SYSTEMD_SERVICE};
|
|
||||||
use niri_config::ConfigPath;
|
use niri_config::ConfigPath;
|
||||||
use niri_ipc::socket::SOCKET_PATH_ENV;
|
use niri_ipc::socket::SOCKET_PATH_ENV;
|
||||||
use portable_atomic::Ordering;
|
use portable_atomic::Ordering;
|
||||||
@@ -230,27 +229,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up config file watcher.
|
watcher::setup(&mut state, &config_path);
|
||||||
let _watcher = {
|
|
||||||
// Parsing the config actually takes > 20 ms on my beefy machine, so let's do it on the
|
|
||||||
// watcher thread.
|
|
||||||
let process = |path: &ConfigPath| {
|
|
||||||
path.load().map_err(|err| {
|
|
||||||
warn!("{err:?}");
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
let (tx, rx) = calloop::channel::sync_channel(1);
|
|
||||||
let watcher = Watcher::new(config_path.clone(), process, tx);
|
|
||||||
event_loop
|
|
||||||
.handle()
|
|
||||||
.insert_source(rx, |event, _, state| match event {
|
|
||||||
calloop::channel::Event::Msg(config) => state.reload_config(config),
|
|
||||||
calloop::channel::Event::Closed => (),
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
watcher
|
|
||||||
};
|
|
||||||
|
|
||||||
// Spawn commands from cli and auto-start.
|
// Spawn commands from cli and auto-start.
|
||||||
spawn(cli.command, None);
|
spawn(cli.command, None);
|
||||||
|
|||||||
@@ -164,6 +164,7 @@ use crate::ui::screen_transition::{self, ScreenTransition};
|
|||||||
use crate::ui::screenshot_ui::{OutputScreenshot, ScreenshotUi, ScreenshotUiRenderElement};
|
use crate::ui::screenshot_ui::{OutputScreenshot, ScreenshotUi, ScreenshotUiRenderElement};
|
||||||
use crate::utils::scale::{closest_representable_scale, guess_monitor_scale};
|
use crate::utils::scale::{closest_representable_scale, guess_monitor_scale};
|
||||||
use crate::utils::spawning::{CHILD_DISPLAY, CHILD_ENV};
|
use crate::utils::spawning::{CHILD_DISPLAY, CHILD_ENV};
|
||||||
|
use crate::utils::watcher::Watcher;
|
||||||
use crate::utils::xwayland::satellite::Satellite;
|
use crate::utils::xwayland::satellite::Satellite;
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
center, center_f64, expand_home, get_monotonic_time, ipc_transform_to_smithay, is_mapped,
|
center, center_f64, expand_home, get_monotonic_time, ipc_transform_to_smithay, is_mapped,
|
||||||
@@ -190,6 +191,8 @@ pub struct Niri {
|
|||||||
/// (and transient changes dropped).
|
/// (and transient changes dropped).
|
||||||
pub config_file_output_config: niri_config::Outputs,
|
pub config_file_output_config: niri_config::Outputs,
|
||||||
|
|
||||||
|
pub config_file_watcher: Option<Watcher>,
|
||||||
|
|
||||||
pub event_loop: LoopHandle<'static, State>,
|
pub event_loop: LoopHandle<'static, State>,
|
||||||
pub scheduler: Scheduler<()>,
|
pub scheduler: Scheduler<()>,
|
||||||
pub stop_signal: LoopSignal,
|
pub stop_signal: LoopSignal,
|
||||||
@@ -2528,6 +2531,7 @@ impl Niri {
|
|||||||
let mut niri = Self {
|
let mut niri = Self {
|
||||||
config,
|
config,
|
||||||
config_file_output_config,
|
config_file_output_config,
|
||||||
|
config_file_watcher: None,
|
||||||
|
|
||||||
event_loop,
|
event_loop,
|
||||||
scheduler,
|
scheduler,
|
||||||
|
|||||||
+40
-20
@@ -1,22 +1,17 @@
|
|||||||
//! File modification watcher.
|
//! File modification watcher.
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::mpsc;
|
||||||
use std::sync::{mpsc, Arc};
|
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
use std::{io, thread};
|
use std::{io, thread};
|
||||||
|
|
||||||
use niri_config::ConfigPath;
|
use niri_config::ConfigPath;
|
||||||
use smithay::reexports::calloop::channel::SyncSender;
|
use smithay::reexports::calloop::channel::SyncSender;
|
||||||
|
|
||||||
pub struct Watcher {
|
use crate::niri::State;
|
||||||
should_stop: Arc<AtomicBool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Watcher {
|
pub struct Watcher {
|
||||||
fn drop(&mut self) {
|
load_config: mpsc::Sender<()>,
|
||||||
self.should_stop.store(true, Ordering::SeqCst);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Watcher {
|
impl Watcher {
|
||||||
@@ -36,10 +31,8 @@ impl Watcher {
|
|||||||
started: Option<mpsc::SyncSender<()>>,
|
started: Option<mpsc::SyncSender<()>>,
|
||||||
polling_interval: Duration,
|
polling_interval: Duration,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let should_stop = Arc::new(AtomicBool::new(false));
|
let (load_config, load_config_rx) = mpsc::channel();
|
||||||
|
|
||||||
{
|
|
||||||
let should_stop = should_stop.clone();
|
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name(format!("Filesystem Watcher for {config_path:?}"))
|
.name(format!("Filesystem Watcher for {config_path:?}"))
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
@@ -80,24 +73,26 @@ impl Watcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
thread::sleep(polling_interval);
|
let mut should_load = match load_config_rx.recv_timeout(polling_interval) {
|
||||||
|
Ok(()) => true,
|
||||||
if should_stop.load(Ordering::SeqCst) {
|
Err(mpsc::RecvTimeoutError::Disconnected) => break,
|
||||||
break;
|
Err(mpsc::RecvTimeoutError::Timeout) => false,
|
||||||
}
|
};
|
||||||
|
|
||||||
if let Ok(new_props) = see(&config_path) {
|
if let Ok(new_props) = see(&config_path) {
|
||||||
if last_props.as_ref() != Some(&new_props) {
|
if last_props.as_ref() != Some(&new_props) {
|
||||||
|
last_props = Some(new_props);
|
||||||
trace!("config file changed");
|
trace!("config file changed");
|
||||||
|
should_load = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if should_load {
|
||||||
let rv = process(&config_path);
|
let rv = process(&config_path);
|
||||||
|
|
||||||
if let Err(err) = changed.send(rv) {
|
if let Err(err) = changed.send(rv) {
|
||||||
warn!("error sending change notification: {err:?}");
|
warn!("error sending change notification: {err:?}");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
last_props = Some(new_props);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,12 +100,37 @@ impl Watcher {
|
|||||||
debug!("exiting watcher thread for {config_path:?}");
|
debug!("exiting watcher thread for {config_path:?}");
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
Self { load_config }
|
||||||
}
|
}
|
||||||
|
|
||||||
Self { should_stop }
|
pub fn load_config(&self) {
|
||||||
|
let _ = self.load_config.send(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn setup(state: &mut State, config_path: &ConfigPath) {
|
||||||
|
// Parsing the config actually takes > 20 ms on my beefy machine, so let's do it on the
|
||||||
|
// watcher thread.
|
||||||
|
let process = |path: &ConfigPath| {
|
||||||
|
path.load().map_err(|err| {
|
||||||
|
warn!("{err:?}");
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let (tx, rx) = calloop::channel::sync_channel(1);
|
||||||
|
state
|
||||||
|
.niri
|
||||||
|
.event_loop
|
||||||
|
.insert_source(rx, |event, _, state| match event {
|
||||||
|
calloop::channel::Event::Msg(config) => state.reload_config(config),
|
||||||
|
calloop::channel::Event::Closed => (),
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
state.niri.config_file_watcher = Some(Watcher::new(config_path.clone(), process, tx));
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|||||||
Reference in New Issue
Block a user