From 702448a2c9f0708e2c3a66d5f719dc218761c4ad Mon Sep 17 00:00:00 2001 From: jar2333 Date: Wed, 4 Jan 2023 17:41:18 -0500 Subject: [PATCH 01/11] added inline versions of shader lisp functions --- src/lisp/mod.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/lisp/mod.rs b/src/lisp/mod.rs index 7740a69..1aa3734 100644 --- a/src/lisp/mod.rs +++ b/src/lisp/mod.rs @@ -231,6 +231,16 @@ fn node( )?; Ok(Val::Node(node_id)) }, + "shader-inline" => { + let (src, width, height, inputs) = shader(graph, env, iter)?; + let node_id = graph.add_shader( + &src, + inputs, + width as u32, + height as u32, + )?; + Ok(Val::Node(node_id)) + }, "shader-param" => { // get the shader we'll be running the transformations // against @@ -262,6 +272,16 @@ fn node( )?; Ok(Val::Node(node_id)) }, + "shader-rec-inline" => { + let (src, width, height, inputs) = shader(graph, env, iter)?; + let node_id = graph.add_rec_shader( + &src, + inputs, + width as u32, + height as u32, + )?; + Ok(Val::Node(node_id)) + }, "extern" => { let (name, inputs) = external(graph, env, iter)?; let adder = env.external(&name)?; From ad1427a90b82a0901d9c61db50020107b881bb6e Mon Sep 17 00:00:00 2001 From: jar2333 Date: Wed, 4 Jan 2023 19:20:41 -0500 Subject: [PATCH 02/11] added reading config from stdin --- src/reload/shader_dir.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/reload/shader_dir.rs b/src/reload/shader_dir.rs index 2217168..21488bd 100644 --- a/src/reload/shader_dir.rs +++ b/src/reload/shader_dir.rs @@ -2,6 +2,7 @@ use std::{ collections::BTreeMap, ffi::OsStr, fs, + io, path::Path, }; @@ -72,12 +73,19 @@ impl ShaderDir { where T: AsRef, { - let lisp = fs::read_to_string(&config).map_err(|_| { - format!( - "Could not read `{}` in shader directory", - config.as_ref().to_str().unwrap() - ) - })?; + let lisp = + if config.as_ref().to_str().unwrap() == "-" { + io::read_to_string(io::stdin()).map_err(|_| { + "Could not read config in stdin" + })? + } else { + fs::read_to_string(&config).map_err(|_| { + format!( + "Could not read `{}` in shader directory", + config.as_ref().to_str().unwrap() + ) + })? + }; let mut shaders = BTreeMap::new(); let files = fs::read_dir(path) From 6a46f56e097e06895e59b57dce5b6eb5e50ff202 Mon Sep 17 00:00:00 2001 From: jar2333 Date: Thu, 5 Jan 2023 01:22:12 -0500 Subject: [PATCH 03/11] added SIGUSR1 reloading --- Cargo.lock | 20 ++++++++++++++++++++ Cargo.toml | 1 + src/reload/watcher.rs | 18 ++++++++++++++++++ 3 files changed, 39 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 7e0bb94..28082ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1500,6 +1500,7 @@ dependencies = [ "include_dir", "lexpr", "notify", + "signal-hook", "structopt", ] @@ -1519,6 +1520,25 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" +[[package]] +name = "signal-hook" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + [[package]] name = "smallvec" version = "1.6.1" diff --git a/Cargo.toml b/Cargo.toml index 50bd73e..102469e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,3 +22,4 @@ lexpr = "0.2.6" include_dir = "0.6" ffmpeg-next = { version = "4.4", optional = true } structopt = "0.3" +signal-hook = "0.3.14" diff --git a/src/reload/watcher.rs b/src/reload/watcher.rs index 031c912..ecd37a4 100644 --- a/src/reload/watcher.rs +++ b/src/reload/watcher.rs @@ -15,15 +15,19 @@ use std::{ Duration, Instant, }, + thread, }; use glium::backend::Context; + use notify::{ RecommendedWatcher, RecursiveMode, Watcher, }; +use signal_hook::{consts::SIGUSR1, iterator::Signals}; + use crate::{ graph::ShaderGraph, lisp::graph_from_sexp, @@ -81,6 +85,20 @@ impl ShaderGraphWatcher { .unwrap(); watcher.watch(&path, RecursiveMode::Recursive).unwrap(); + let signals = Signals::new(&[SIGUSR1]); + match signals { + Ok(mut s) => { + let changed = changed.clone(); + thread::spawn(move || { + for sig in s.forever() { + changed.store(true, Ordering::SeqCst); + println!("Received signal {:?}", sig); + } + }); + } + Err(e) => println!("[warn] Signal listen error: `{:?}`.", e) + }; + let shader_graph = ShaderGraphWatcher::build(context, &path, &config)?; let last_reload = Instant::now(); From 50763e2563568a8afda6d9f9c6776539178d8fb5 Mon Sep 17 00:00:00 2001 From: jar2333 Date: Thu, 5 Jan 2023 02:22:58 -0500 Subject: [PATCH 04/11] added some convention to log print --- src/reload/watcher.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reload/watcher.rs b/src/reload/watcher.rs index ecd37a4..7884796 100644 --- a/src/reload/watcher.rs +++ b/src/reload/watcher.rs @@ -92,7 +92,7 @@ impl ShaderGraphWatcher { thread::spawn(move || { for sig in s.forever() { changed.store(true, Ordering::SeqCst); - println!("Received signal {:?}", sig); + println!("[info] Received signal {:?}", sig); } }); } From 806f281b5bef3f1189982bf05f414fb6d9f38bb1 Mon Sep 17 00:00:00 2001 From: jar2333 Date: Fri, 6 Jan 2023 02:38:41 -0500 Subject: [PATCH 05/11] added stdin consumption of config files --- src/reload/shader_dir.rs | 87 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 3 deletions(-) diff --git a/src/reload/shader_dir.rs b/src/reload/shader_dir.rs index 21488bd..f1bfba5 100644 --- a/src/reload/shader_dir.rs +++ b/src/reload/shader_dir.rs @@ -4,6 +4,7 @@ use std::{ fs, io, path::Path, + io::BufRead, }; use include_dir::{ @@ -73,10 +74,10 @@ impl ShaderDir { where T: AsRef, { - let lisp = + let lisp = //custom stdin s-expression consuming function here if config.as_ref().to_str().unwrap() == "-" { - io::read_to_string(io::stdin()).map_err(|_| { - "Could not read config in stdin" + read_stdin_config().map_err(|s| { + format!("Could not read config in stdin: {}", s) })? } else { fs::read_to_string(&config).map_err(|_| { @@ -115,3 +116,83 @@ impl ShaderDir { Ok(ShaderDir { lisp, shaders }) } } + +fn read_stdin_config() -> Result { + let mut byte_vec: Vec = Vec::new(); + let stdin = io::stdin(); // We get `Stdin` here. + + { + let mut handle = stdin.lock(); + + let mut count = 0; + let mut start = 0; + + //consume ONE s-expression + loop { + let bytes_read = handle.read_until(b')', &mut byte_vec); + match bytes_read { + Ok(read) => { + //count the number of opening parenthesis in read bytes + let len = byte_vec.len(); + for i in len-read..len { + if b'(' == byte_vec[i] { + count += 1; + } + }; + }, + Err(err) => return Err(format!("Reading STDIN error: {}", err)), + }; + + count -= 1; + + if count < 0 { + return Err("Unbound s-expression error".to_string()); + } else if count > 0 { + continue; + } else { + //check if s-expression is an output one + if did_read_output_sexpr(&byte_vec, start) { + break; + } else { + start = byte_vec.len() - 1; + } + } + } + } + + //All s-expressions read, convert to string + let is_valid_utf8 = std::str::from_utf8(&byte_vec); + match is_valid_utf8 { + Ok(the_str) => Ok(the_str.to_string()), + Err(err) => Err(format!("Parsing STDIN utf8 error: {}", err)), + } +} + +fn did_read_output_sexpr(bytes : &Vec, start : usize) -> bool { + let mut i = start; + + //skip everything before initial '(' + while bytes[i] != b'(' { + i += 1; + } + + //skip '(' + i += 1; + + //find index past initial '(' and past all whitespace + while (bytes[i] as char).is_whitespace() { + i += 1; + } + + //check if the rest of the bytes Vec has enough space for 'output', if not, false + if bytes.len() - i < 6 { + return false; + } + + //convert slice to str and use equality comparison + let maybe_str = std::str::from_utf8(&bytes[i..i+6]); + match maybe_str { + Ok(s) => s == "output", + Err(_) => false, + } +} \ No newline at end of file From 52cfb5a1f0609a23fccfd14d56627f20c7811773 Mon Sep 17 00:00:00 2001 From: jar2333 Date: Fri, 6 Jan 2023 13:20:57 -0500 Subject: [PATCH 06/11] added conditional compilation for usage of signal_hook crate and the SIGUSR1 handling --- Cargo.toml | 2 ++ src/reload/watcher.rs | 30 +++++++++++++++++------------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 102469e..c53f08f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,4 +22,6 @@ lexpr = "0.2.6" include_dir = "0.6" ffmpeg-next = { version = "4.4", optional = true } structopt = "0.3" + +[target.'cfg(unix)'.dependencies] signal-hook = "0.3.14" diff --git a/src/reload/watcher.rs b/src/reload/watcher.rs index 7884796..260b872 100644 --- a/src/reload/watcher.rs +++ b/src/reload/watcher.rs @@ -26,6 +26,7 @@ use notify::{ Watcher, }; +#[cfg(target_family = "unix")] use signal_hook::{consts::SIGUSR1, iterator::Signals}; use crate::{ @@ -85,19 +86,22 @@ impl ShaderGraphWatcher { .unwrap(); watcher.watch(&path, RecursiveMode::Recursive).unwrap(); - let signals = Signals::new(&[SIGUSR1]); - match signals { - Ok(mut s) => { - let changed = changed.clone(); - thread::spawn(move || { - for sig in s.forever() { - changed.store(true, Ordering::SeqCst); - println!("[info] Received signal {:?}", sig); - } - }); - } - Err(e) => println!("[warn] Signal listen error: `{:?}`.", e) - }; + #[cfg(target_family = "unix")] + { + let signals = Signals::new(&[SIGUSR1]); + match signals { + Ok(mut s) => { + let changed = changed.clone(); + thread::spawn(move || { + for sig in s.forever() { + changed.store(true, Ordering::SeqCst); + println!("[info] Received signal {:?}", sig); + } + }); + } + Err(e) => println!("[warn] Signal listen error: `{:?}`.", e) + }; + } let shader_graph = ShaderGraphWatcher::build(context, &path, &config)?; let last_reload = Instant::now(); From ae710aa9f3df89e6f58955634cba3e0208ddf08f Mon Sep 17 00:00:00 2001 From: jar2333 Date: Fri, 6 Jan 2023 20:40:27 -0500 Subject: [PATCH 07/11] added comments, removed some duplication --- src/reload/shader_dir.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/reload/shader_dir.rs b/src/reload/shader_dir.rs index f1bfba5..4ed0585 100644 --- a/src/reload/shader_dir.rs +++ b/src/reload/shader_dir.rs @@ -74,16 +74,18 @@ impl ShaderDir { where T: AsRef, { + let config_str = config.as_ref().to_str().unwrap(); + let lisp = //custom stdin s-expression consuming function here - if config.as_ref().to_str().unwrap() == "-" { + if config_str == "-" { read_stdin_config().map_err(|s| { - format!("Could not read config in stdin: {}", s) + format!("Could not read config from stdin: {}", s) })? } else { fs::read_to_string(&config).map_err(|_| { format!( "Could not read `{}` in shader directory", - config.as_ref().to_str().unwrap() + config_str ) })? }; @@ -124,7 +126,7 @@ fn read_stdin_config() -> Result { { let mut handle = stdin.lock(); - let mut count = 0; + let mut count = 0; //the number of open parentheses with no corresponding closing let mut start = 0; //consume ONE s-expression @@ -143,12 +145,13 @@ fn read_stdin_config() -> Result { Err(err) => return Err(format!("Reading STDIN error: {}", err)), }; + //subtract 1 since we reached a closing parenthesis count -= 1; if count < 0 { return Err("Unbound s-expression error".to_string()); } else if count > 0 { - continue; + continue; //s-expression not finished } else { //check if s-expression is an output one if did_read_output_sexpr(&byte_vec, start) { From 71e49875404c69057211669178183a58e401e2fd Mon Sep 17 00:00:00 2001 From: jar2333 Date: Sat, 7 Jan 2023 00:02:15 -0500 Subject: [PATCH 08/11] stdin reading thread boilerplate (not functional yet) --- src/reload/shader_dir.rs | 91 ++------------------------------ src/reload/watcher.rs | 110 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 86 deletions(-) diff --git a/src/reload/shader_dir.rs b/src/reload/shader_dir.rs index 4ed0585..2c7ce92 100644 --- a/src/reload/shader_dir.rs +++ b/src/reload/shader_dir.rs @@ -2,9 +2,8 @@ use std::{ collections::BTreeMap, ffi::OsStr, fs, - io, path::Path, - io::BufRead, + }; use include_dir::{ @@ -78,9 +77,10 @@ impl ShaderDir { let lisp = //custom stdin s-expression consuming function here if config_str == "-" { - read_stdin_config().map_err(|s| { - format!("Could not read config from stdin: {}", s) - })? + // read_stdin_config().map_err(|s| { + // format!("Could not read config from stdin: {}", s) + // })? + return Err("Debug".to_string()); } else { fs::read_to_string(&config).map_err(|_| { format!( @@ -117,85 +117,4 @@ impl ShaderDir { Ok(ShaderDir { lisp, shaders }) } -} - -fn read_stdin_config() -> Result { - let mut byte_vec: Vec = Vec::new(); - let stdin = io::stdin(); // We get `Stdin` here. - - { - let mut handle = stdin.lock(); - - let mut count = 0; //the number of open parentheses with no corresponding closing - let mut start = 0; - - //consume ONE s-expression - loop { - let bytes_read = handle.read_until(b')', &mut byte_vec); - match bytes_read { - Ok(read) => { - //count the number of opening parenthesis in read bytes - let len = byte_vec.len(); - for i in len-read..len { - if b'(' == byte_vec[i] { - count += 1; - } - }; - }, - Err(err) => return Err(format!("Reading STDIN error: {}", err)), - }; - - //subtract 1 since we reached a closing parenthesis - count -= 1; - - if count < 0 { - return Err("Unbound s-expression error".to_string()); - } else if count > 0 { - continue; //s-expression not finished - } else { - //check if s-expression is an output one - if did_read_output_sexpr(&byte_vec, start) { - break; - } else { - start = byte_vec.len() - 1; - } - } - } - } - - //All s-expressions read, convert to string - let is_valid_utf8 = std::str::from_utf8(&byte_vec); - match is_valid_utf8 { - Ok(the_str) => Ok(the_str.to_string()), - Err(err) => Err(format!("Parsing STDIN utf8 error: {}", err)), - } -} - -fn did_read_output_sexpr(bytes : &Vec, start : usize) -> bool { - let mut i = start; - - //skip everything before initial '(' - while bytes[i] != b'(' { - i += 1; - } - - //skip '(' - i += 1; - - //find index past initial '(' and past all whitespace - while (bytes[i] as char).is_whitespace() { - i += 1; - } - - //check if the rest of the bytes Vec has enough space for 'output', if not, false - if bytes.len() - i < 6 { - return false; - } - - //convert slice to str and use equality comparison - let maybe_str = std::str::from_utf8(&bytes[i..i+6]); - match maybe_str { - Ok(s) => s == "output", - Err(_) => false, - } } \ No newline at end of file diff --git a/src/reload/watcher.rs b/src/reload/watcher.rs index 260b872..243bad6 100644 --- a/src/reload/watcher.rs +++ b/src/reload/watcher.rs @@ -16,6 +16,10 @@ use std::{ Instant, }, thread, + sync::mpsc::{Sender, Receiver}, + sync::mpsc, + io, + io::BufRead, }; use glium::backend::Context; @@ -47,6 +51,7 @@ pub struct ShaderGraphWatcher { changed: Arc, _watcher: RecommendedWatcher, shader_graph: ShaderGraph, + _stdin_rx: Receiver } pub enum WatchResult { @@ -75,6 +80,7 @@ impl ShaderGraphWatcher { let config = config.as_ref().to_path_buf(); let changed = Arc::new(AtomicBool::new(false)); + // build the watcher let mut watcher = RecommendedWatcher::new({ let changed = changed.clone(); @@ -86,6 +92,28 @@ impl ShaderGraphWatcher { .unwrap(); watcher.watch(&path, RecursiveMode::Recursive).unwrap(); + // stdin reading thread + let (tx, rx): (Sender, Receiver) = mpsc::channel(); + { + // let thread_tx = tx.clone(); + let changed = changed.clone(); + thread::spawn(move || { + loop { + println!("[info] Reading config from STDIN"); + let maybe_config = read_stdin_config(); + match maybe_config { + Ok(c) => { + println!("[info] STDIN config received, sending to receiver"); + tx.send(c).unwrap(); + changed.store(true, Ordering::SeqCst); + }, + Err(e) => println!("[warn] STDIN config read error: `{:?}`.", e), + } + } + }); + } + + // SIGUSR handling thread #[cfg(target_family = "unix")] { let signals = Signals::new(&[SIGUSR1]); @@ -114,6 +142,7 @@ impl ShaderGraphWatcher { changed, _watcher: watcher, shader_graph, + _stdin_rx: rx }) } @@ -171,3 +200,84 @@ impl ShaderGraphWatcher { } } } + +fn read_stdin_config() -> Result { + let mut byte_vec: Vec = Vec::new(); + let stdin = io::stdin(); // We get `Stdin` here. + + { + let mut handle = stdin.lock(); + + let mut count = 0; //the number of open parentheses with no corresponding closing + let mut start = 0; + + //consume ONE s-expression + loop { + let bytes_read = handle.read_until(b')', &mut byte_vec); + match bytes_read { + Ok(read) => { + //count the number of opening parenthesis in read bytes + let len = byte_vec.len(); + for i in len-read..len { + if b'(' == byte_vec[i] { + count += 1; + } + }; + }, + Err(err) => return Err(format!("Reading STDIN error: {}", err)), + }; + + //subtract 1 since we reached a closing parenthesis + count -= 1; + + if count < 0 { + return Err("Unbound s-expression error".to_string()); + } else if count > 0 { + continue; //s-expression not finished + } else { + //check if s-expression is an output one + if did_read_output_sexpr(&byte_vec, start) { + break; + } else { + start = byte_vec.len() - 1; + } + } + } + } + + //All s-expressions read, convert to string + let is_valid_utf8 = std::str::from_utf8(&byte_vec); + match is_valid_utf8 { + Ok(the_str) => Ok(the_str.to_string()), + Err(err) => Err(format!("Parsing STDIN utf8 error: {}", err)), + } +} + +fn did_read_output_sexpr(bytes : &Vec, start : usize) -> bool { + let mut i = start; + + //skip everything before initial '(' + while bytes[i] != b'(' { + i += 1; + } + + //skip '(' + i += 1; + + //find index past initial '(' and past all whitespace + while (bytes[i] as char).is_whitespace() { + i += 1; + } + + //check if the rest of the bytes Vec has enough space for 'output', if not, false + if bytes.len() - i < 6 { + return false; + } + + //convert slice to str and use equality comparison + let maybe_str = std::str::from_utf8(&bytes[i..i+6]); + match maybe_str { + Ok(s) => s == "output", + Err(_) => false, + } +} \ No newline at end of file From ca1f2fd2a6f3667a6d38a63140465a3eba43b0ee Mon Sep 17 00:00:00 2001 From: jar2333 Date: Sat, 7 Jan 2023 01:54:36 -0500 Subject: [PATCH 09/11] added stdin reload --- src/main.rs | 14 +------ src/reload/shader_dir.rs | 19 +-------- src/reload/watcher.rs | 87 +++++++++++++++++++++++++++++----------- 3 files changed, 68 insertions(+), 52 deletions(-) diff --git a/src/main.rs b/src/main.rs index ee9c595..6f44464 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,11 +21,10 @@ use glium::{ Surface, }; use shadergarden::{ - lisp, - map, png, reload, util, + reload::watcher::ShaderGraphWatcher }; use structopt::{ clap::AppSettings, @@ -166,16 +165,7 @@ fn render(render: Render) { ); // set up hot code reloading - let shader_dir = reload::ShaderDir::new_from_dir(args.project, lisp_config) - .expect("Could not load initial shader directory"); - let mut graph = - lisp::graph_from_sexp(display.get_context(), shader_dir, map! {}) - .map_err(|e| { - eprintln!("[fatal] Could not build initial graph:"); - eprintln!("{}", e); - panic!(); - }) - .unwrap(); + let mut graph = ShaderGraphWatcher::build_initial(display.get_context(), &args.project, &lisp_config).unwrap(); eprintln!("[info] Built initial graph"); diff --git a/src/reload/shader_dir.rs b/src/reload/shader_dir.rs index 2c7ce92..687aca0 100644 --- a/src/reload/shader_dir.rs +++ b/src/reload/shader_dir.rs @@ -3,7 +3,6 @@ use std::{ ffi::OsStr, fs, path::Path, - }; use include_dir::{ @@ -69,26 +68,12 @@ impl ShaderDir { } /// Creates a new `ShaderDir` from a directory. - pub fn new_from_dir(path: T, config: T) -> Result + pub fn new_from_dir(path: T, get_lisp : impl Fn() -> Result) -> Result where T: AsRef, { - let config_str = config.as_ref().to_str().unwrap(); - let lisp = //custom stdin s-expression consuming function here - if config_str == "-" { - // read_stdin_config().map_err(|s| { - // format!("Could not read config from stdin: {}", s) - // })? - return Err("Debug".to_string()); - } else { - fs::read_to_string(&config).map_err(|_| { - format!( - "Could not read `{}` in shader directory", - config_str - ) - })? - }; + let lisp = get_lisp()?; let mut shaders = BTreeMap::new(); let files = fs::read_dir(path) diff --git a/src/reload/watcher.rs b/src/reload/watcher.rs index 243bad6..2bb3b1d 100644 --- a/src/reload/watcher.rs +++ b/src/reload/watcher.rs @@ -20,6 +20,7 @@ use std::{ sync::mpsc, io, io::BufRead, + fs, }; use glium::backend::Context; @@ -92,7 +93,29 @@ impl ShaderGraphWatcher { .unwrap(); watcher.watch(&path, RecursiveMode::Recursive).unwrap(); - // stdin reading thread + // SIGUSR handling thread + #[cfg(target_family = "unix")] + { + let signals = Signals::new(&[SIGUSR1]); + match signals { + Ok(mut s) => { + let changed = changed.clone(); + thread::spawn(move || { + for sig in s.forever() { + changed.store(true, Ordering::SeqCst); + println!("[info] Received signal {:?}", sig); + } + }); + } + Err(e) => println!("[warn] Signal listen error: `{:?}`.", e) + }; + } + + //initial build + let shader_graph = ShaderGraphWatcher::build_initial(context, &path, &config)?; + let last_reload = Instant::now(); + + // STDIN reading thread let (tx, rx): (Sender, Receiver) = mpsc::channel(); { // let thread_tx = tx.clone(); @@ -113,27 +136,6 @@ impl ShaderGraphWatcher { }); } - // SIGUSR handling thread - #[cfg(target_family = "unix")] - { - let signals = Signals::new(&[SIGUSR1]); - match signals { - Ok(mut s) => { - let changed = changed.clone(); - thread::spawn(move || { - for sig in s.forever() { - changed.store(true, Ordering::SeqCst); - println!("[info] Received signal {:?}", sig); - } - }); - } - Err(e) => println!("[warn] Signal listen error: `{:?}`.", e) - }; - } - - let shader_graph = ShaderGraphWatcher::build(context, &path, &config)?; - let last_reload = Instant::now(); - Ok(ShaderGraphWatcher { context: context.clone(), last_reload, @@ -146,12 +148,50 @@ impl ShaderGraphWatcher { }) } + // TO-DO: reduce some code duplication here (build => build_initial AND build_reload) + pub fn build_initial( + context: &Rc, + path: &Path, + config: &Path, + ) -> Result { + let shader_dir = match config.to_str().unwrap() { + "-" => + ShaderDir::new_from_dir(path, || { + read_stdin_config().map_err(|s| { + format!("Could not read config from stdin: {}", s) + }) + })?, + cfg => + ShaderDir::new_from_dir(path, || { + fs::read_to_string(&config).map_err(|_| { + format!("Could not read `{}` in shader directory", cfg) + }) + })?, + }; + let shader_graph = graph_from_sexp(context, shader_dir, map! {})?; + Ok(shader_graph) + } + fn build( context: &Rc, path: &Path, config: &Path, + rx: &Receiver, ) -> Result { - let shader_dir = ShaderDir::new_from_dir(path, config)?; + let shader_dir = match config.to_str().unwrap() { + "-" => + ShaderDir::new_from_dir(path, || { + rx.recv().map_err(|s| { + format!("Could not read config from stdin: {}", s) + }) + })?, + cfg => + ShaderDir::new_from_dir(path, || { + fs::read_to_string(&config).map_err(|_| { + format!("Could not read `{}` in shader directory", cfg) + }) + })?, + }; let shader_graph = graph_from_sexp(context, shader_dir, map! {})?; Ok(shader_graph) } @@ -173,6 +213,7 @@ impl ShaderGraphWatcher { &self.context, &self.path, &self.config, + &self._stdin_rx, ) { Ok(graph) => { self.shader_graph = graph; From db58c0cbc2b9459f532799e3c8bdbd8cabda643c Mon Sep 17 00:00:00 2001 From: jar2333 Date: Sat, 7 Jan 2023 23:03:03 -0500 Subject: [PATCH 10/11] stdin thread only executes for -, terminates on error --- src/reload/watcher.rs | 69 +++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/src/reload/watcher.rs b/src/reload/watcher.rs index 2bb3b1d..a904bbe 100644 --- a/src/reload/watcher.rs +++ b/src/reload/watcher.rs @@ -52,7 +52,7 @@ pub struct ShaderGraphWatcher { changed: Arc, _watcher: RecommendedWatcher, shader_graph: ShaderGraph, - _stdin_rx: Receiver + stdin_rx: Option> } pub enum WatchResult { @@ -116,25 +116,34 @@ impl ShaderGraphWatcher { let last_reload = Instant::now(); // STDIN reading thread - let (tx, rx): (Sender, Receiver) = mpsc::channel(); - { - // let thread_tx = tx.clone(); - let changed = changed.clone(); - thread::spawn(move || { - loop { - println!("[info] Reading config from STDIN"); - let maybe_config = read_stdin_config(); - match maybe_config { - Ok(c) => { - println!("[info] STDIN config received, sending to receiver"); - tx.send(c).unwrap(); - changed.store(true, Ordering::SeqCst); - }, - Err(e) => println!("[warn] STDIN config read error: `{:?}`.", e), - } + let stdin_rx = match config.to_str().unwrap() { + "-" => { + let (tx, rx): (Sender, Receiver) = mpsc::channel(); + { + // let thread_tx = tx.clone(); + let changed = changed.clone(); + thread::spawn(move || { + loop { + println!("[info] Reading config from STDIN"); + let maybe_config = read_stdin_config(); + match maybe_config { + Ok(c) => { + println!("[info] STDIN config received, sending to receiver"); + tx.send(c).unwrap(); + changed.store(true, Ordering::SeqCst); + }, + Err(e) => { + println!("[warn] STDIN config read error: `{:?}`.", e); + return; + }, + } + } + }); } - }); - } + Some(rx) + }, + _ => None, + }; Ok(ShaderGraphWatcher { context: context.clone(), @@ -144,11 +153,12 @@ impl ShaderGraphWatcher { changed, _watcher: watcher, shader_graph, - _stdin_rx: rx + stdin_rx: stdin_rx }) } // TO-DO: reduce some code duplication here (build => build_initial AND build_reload) + //Refactor with helper methods, maybe in a separate file. pub fn build_initial( context: &Rc, path: &Path, @@ -172,23 +182,25 @@ impl ShaderGraphWatcher { Ok(shader_graph) } - fn build( + fn build_reload( context: &Rc, path: &Path, config: &Path, - rx: &Receiver, + stdin_rx: &Option>, ) -> Result { - let shader_dir = match config.to_str().unwrap() { - "-" => + let shader_dir = match stdin_rx { + Some(rx) => ShaderDir::new_from_dir(path, || { rx.recv().map_err(|s| { format!("Could not read config from stdin: {}", s) }) })?, - cfg => + None => ShaderDir::new_from_dir(path, || { fs::read_to_string(&config).map_err(|_| { - format!("Could not read `{}` in shader directory", cfg) + format!("Could not read `{}` in shader directory", + config.to_str().unwrap() + ) }) })?, }; @@ -209,11 +221,11 @@ impl ShaderGraphWatcher { /// loop! As with `graph_no_reload`, only use this /// for fine-grained control over reloads. pub fn graph_force_reload(&mut self) -> (&mut ShaderGraph, WatchResult) { - let watch_result = match ShaderGraphWatcher::build( + let watch_result = match ShaderGraphWatcher::build_reload( &self.context, &self.path, &self.config, - &self._stdin_rx, + &self.stdin_rx, ) { Ok(graph) => { self.shader_graph = graph; @@ -256,6 +268,7 @@ fn read_stdin_config() -> Result { loop { let bytes_read = handle.read_until(b')', &mut byte_vec); match bytes_read { + Ok(0) => return Err("Nothing read error (STDIN closed?)".to_string()), Ok(read) => { //count the number of opening parenthesis in read bytes let len = byte_vec.len(); From 6e7e5ca911f4047383981861511b618650652270 Mon Sep 17 00:00:00 2001 From: jar2333 Date: Sun, 8 Jan 2023 23:55:15 -0500 Subject: [PATCH 11/11] caught a specific error condition in the stdin reading function --- src/reload/watcher.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/reload/watcher.rs b/src/reload/watcher.rs index a904bbe..19fc4e2 100644 --- a/src/reload/watcher.rs +++ b/src/reload/watcher.rs @@ -268,9 +268,16 @@ fn read_stdin_config() -> Result { loop { let bytes_read = handle.read_until(b')', &mut byte_vec); match bytes_read { - Ok(0) => return Err("Nothing read error (STDIN closed?)".to_string()), + Ok(0) => { + return Err("No bytes were read (STDIN closed?)".to_string()); + }, + Ok(_) if *byte_vec.last().unwrap() != b')' => { + return Err("Last byte read not ')' (STDIN closed?)".to_string()); + }, Ok(read) => { //count the number of opening parenthesis in read bytes + // println!("Number of bytes read: {}", read); + // println!("Last byte read: {}", *(byte_vec.last().unwrap()) as char); let len = byte_vec.len(); for i in len-read..len { if b'(' == byte_vec[i] { @@ -278,7 +285,9 @@ fn read_stdin_config() -> Result { } }; }, - Err(err) => return Err(format!("Reading STDIN error: {}", err)), + Err(err) => { + return Err(format!("Reading STDIN error: {}", err)); + }, }; //subtract 1 since we reached a closing parenthesis