Critical: fix unsquashfs arg order, quote Exec paths with spaces, fix compare_versions antisymmetry, chunk-based signature detection, bounded ELF header reads. High: handle NULL CVE severity, prevent pipe deadlock in inspector, fix glob_match edge case, fix backup archive path collisions, async crash detection with stderr capture. Medium: gate scan on auto-scan setting, fix window size persistence, fix announce() for Stack containers, claim lightbox gesture, use serde_json for CLI output, remove dead CSS @media blocks, add detail-tab persistence, remove invalid metainfo categories, byte-level fuse signature search. Low: tighten Wayland env var detection, ELF magic validation, timeout for update info extraction, quoted arg parsing, stop watcher timer on window destroy, GSettings choices/range constraints, remove unused CSS classes, define status-ok/status-attention CSS.
80 lines
2.5 KiB
Rust
80 lines
2.5 KiB
Rust
use std::path::PathBuf;
|
|
use std::sync::mpsc;
|
|
use std::time::Duration;
|
|
|
|
use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
|
|
|
|
/// Events sent from the file watcher to the UI thread.
|
|
#[derive(Debug, Clone)]
|
|
pub enum WatchEvent {
|
|
/// One or more AppImage files were created, modified, or deleted.
|
|
Changed(Vec<PathBuf>),
|
|
}
|
|
|
|
/// Start watching the given directories for AppImage file changes.
|
|
/// Returns the watcher handle (must be kept alive).
|
|
/// The callback `on_event` is invoked on the background debounce thread.
|
|
pub fn start_watcher<F: Fn(WatchEvent) + Send + 'static>(
|
|
dirs: Vec<PathBuf>,
|
|
on_event: F,
|
|
) -> Option<RecommendedWatcher> {
|
|
let (notify_tx, notify_rx) = mpsc::channel::<Result<Event, notify::Error>>();
|
|
|
|
let mut watcher = RecommendedWatcher::new(
|
|
move |res| {
|
|
notify_tx.send(res).ok();
|
|
},
|
|
Config::default().with_poll_interval(Duration::from_secs(2)),
|
|
).ok()?;
|
|
|
|
for dir in &dirs {
|
|
if dir.is_dir() {
|
|
watcher.watch(dir, RecursiveMode::NonRecursive).ok();
|
|
}
|
|
}
|
|
|
|
// Spawn a thread to debounce and forward events
|
|
std::thread::spawn(move || {
|
|
let mut pending: Vec<PathBuf> = Vec::new();
|
|
let debounce = Duration::from_millis(500);
|
|
|
|
loop {
|
|
match notify_rx.recv_timeout(debounce) {
|
|
Ok(Ok(event)) => {
|
|
if is_appimage_event(&event) {
|
|
for path in event.paths {
|
|
if !pending.contains(&path) {
|
|
pending.push(path);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Ok(Err(_)) => {}
|
|
Err(mpsc::RecvTimeoutError::Timeout) => {
|
|
if !pending.is_empty() {
|
|
let paths = std::mem::take(&mut pending);
|
|
on_event(WatchEvent::Changed(paths));
|
|
}
|
|
}
|
|
Err(mpsc::RecvTimeoutError::Disconnected) => break,
|
|
}
|
|
}
|
|
});
|
|
|
|
Some(watcher)
|
|
}
|
|
|
|
fn is_appimage_event(event: &Event) -> bool {
|
|
match event.kind {
|
|
EventKind::Create(_) | EventKind::Remove(_) | EventKind::Modify(_) => {
|
|
event.paths.iter().any(|p| {
|
|
p.extension()
|
|
.and_then(|e| e.to_str())
|
|
.map(|e| e.eq_ignore_ascii_case("appimage"))
|
|
.unwrap_or(false)
|
|
})
|
|
}
|
|
_ => false,
|
|
}
|
|
}
|