use std::{
path::{
Path,
PathBuf,
},
rc::Rc,
sync::{
atomic::{
AtomicBool,
Ordering,
},
Arc,
},
time::{
Duration,
Instant,
},
};
use glium::backend::Context;
use notify::{
RecommendedWatcher,
RecursiveMode,
Watcher,
};
use crate::{
graph::ShaderGraph,
lisp::graph_from_sexp,
map,
reload::ShaderDir,
};
pub struct ShaderGraphWatcher {
context: Rc<Context>,
last_reload: Instant,
path: PathBuf,
config: PathBuf,
changed: Arc<AtomicBool>,
_watcher: RecommendedWatcher,
shader_graph: ShaderGraph,
}
pub enum WatchResult {
NoChange,
Rebuilt,
Err(String),
}
impl ShaderGraphWatcher {
pub fn new_watch_dir<T>(
context: &Rc<Context>,
path: T,
config: T,
) -> Result<ShaderGraphWatcher, String>
where
T: AsRef<Path>,
{
let path = path.as_ref().to_path_buf();
let config = config.as_ref().to_path_buf();
let changed = Arc::new(AtomicBool::new(false));
let mut watcher = RecommendedWatcher::new({
let changed = changed.clone();
move |res| match res {
Ok(_) => changed.store(true, Ordering::SeqCst),
Err(e) => println!("[warn] Watch error: `{:?}`.", e),
}
})
.unwrap();
watcher.watch(&path, RecursiveMode::Recursive).unwrap();
let shader_graph = ShaderGraphWatcher::build(context, &path, &config)?;
let last_reload = Instant::now();
Ok(ShaderGraphWatcher {
context: context.clone(),
last_reload,
path,
config,
changed,
_watcher: watcher,
shader_graph,
})
}
fn build(
context: &Rc<Context>,
path: &Path,
config: &Path,
) -> Result<ShaderGraph, String> {
let shader_dir = ShaderDir::new_from_dir(path, config)?;
let shader_graph = graph_from_sexp(context, shader_dir, map! {})?;
Ok(shader_graph)
}
pub fn graph_no_reload(&mut self) -> &mut ShaderGraph {
&mut self.shader_graph
}
pub fn graph_force_reload(&mut self) -> (&mut ShaderGraph, WatchResult) {
let watch_result = match ShaderGraphWatcher::build(
&self.context,
&self.path,
&self.config,
) {
Ok(graph) => {
self.shader_graph = graph;
WatchResult::Rebuilt
},
Err(error) => WatchResult::Err(error),
};
self.last_reload = Instant::now();
(&mut self.shader_graph, watch_result)
}
pub fn graph(&mut self) -> (&mut ShaderGraph, WatchResult) {
if self.last_reload.elapsed() > Duration::from_millis(300)
&& self.changed.swap(false, Ordering::SeqCst)
{
self.graph_force_reload()
} else {
(self.graph_no_reload(), WatchResult::NoChange)
}
}
}