bread-theme 0.2.8: fix live reload — watch the dir, not the file
The stylesheet is written with write-tmp-then-rename (atomic), which replaces the inode. A monitor on the file itself caught the first replace then went deaf (inotify reports DELETE_SELF and never re-arms), so `bread-theme reload` updated the file but no running GUI ever recoloured. Monitor the parent directory and filter for the stylesheet filename instead — that fires on every reload. Verified against a real atomic-rename write (event arrives as Renamed with the new name in other_file, so match both file and other_file). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
ea87083c06
commit
77417d5521
1 changed files with 29 additions and 10 deletions
|
|
@ -26,6 +26,33 @@ fn reload_app() {
|
|||
}
|
||||
}
|
||||
|
||||
/// Watch the shared stylesheet for changes and run `reload` when it's rewritten.
|
||||
///
|
||||
/// `bread-theme` writes the file with write-tmp-then-rename (atomic), which
|
||||
/// *replaces the inode*. A monitor on the file itself dies after the first
|
||||
/// replace (inotify reports DELETE_SELF and never re-arms), so we monitor the
|
||||
/// parent *directory* and filter for the stylesheet's filename — that fires
|
||||
/// reliably on every reload. Returns the monitor (keep it alive to stay armed).
|
||||
fn watch_theme_file(reload: fn()) -> Option<gio::FileMonitor> {
|
||||
let target = crate::shared_css_path();
|
||||
let dir = target.parent()?;
|
||||
// The dir must exist to be monitored; `bread-theme generate` makes it at
|
||||
// login, but create it here too so a GUI started first still arms the watch.
|
||||
let _ = std::fs::create_dir_all(dir);
|
||||
let monitor = gio::File::for_path(dir)
|
||||
.monitor_directory(gio::FileMonitorFlags::WATCH_MOVES, gio::Cancellable::NONE)
|
||||
.ok()?;
|
||||
monitor.connect_changed(move |_, file, other, _event| {
|
||||
// The rename lands as an event whose file (or move destination) is the
|
||||
// stylesheet. Match either to catch both CREATED/CHANGED and MOVED_IN.
|
||||
let is_target = |f: &gio::File| f.path().as_deref() == Some(target.as_path());
|
||||
if is_target(file) || other.is_some_and(is_target) {
|
||||
reload();
|
||||
}
|
||||
});
|
||||
Some(monitor)
|
||||
}
|
||||
|
||||
/// Apply an app's *own* stylesheet and keep it live across palette changes.
|
||||
///
|
||||
/// `build` is called now to produce the app-specific CSS, and again every time
|
||||
|
|
@ -47,11 +74,7 @@ pub fn apply_app_css<F: Fn() -> String + 'static>(build: F) {
|
|||
if cell.borrow().is_some() {
|
||||
return;
|
||||
}
|
||||
let file = gio::File::for_path(crate::shared_css_path());
|
||||
if let Ok(monitor) = file.monitor_file(gio::FileMonitorFlags::NONE, gio::Cancellable::NONE) {
|
||||
monitor.connect_changed(|_, _, _, _| reload_app());
|
||||
*cell.borrow_mut() = Some(monitor);
|
||||
}
|
||||
*cell.borrow_mut() = watch_theme_file(reload_app);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -68,11 +91,7 @@ pub fn apply_shared() {
|
|||
if cell.borrow().is_some() {
|
||||
return;
|
||||
}
|
||||
let file = gio::File::for_path(crate::shared_css_path());
|
||||
if let Ok(monitor) = file.monitor_file(gio::FileMonitorFlags::NONE, gio::Cancellable::NONE) {
|
||||
monitor.connect_changed(|_, _, _, _| reload_shared());
|
||||
*cell.borrow_mut() = Some(monitor);
|
||||
}
|
||||
*cell.borrow_mut() = watch_theme_file(reload_shared);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue