diff --git a/Cargo.lock b/Cargo.lock index 4f4c7a0..0059edf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2122,6 +2122,7 @@ dependencies = [ "futures-core", "pin-project-lite", "tokio", + "tokio-util", ] [[package]] @@ -2320,6 +2321,7 @@ dependencies = [ "pico-args", "same-file", "tokio", + "tokio-stream", "uw8-tool", "warp", "wasmtime", diff --git a/Cargo.toml b/Cargo.toml index 8dc4e2e..3672d07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,4 +17,5 @@ uw8-tool = { path = "uw8-tool" } same-file = "1" warp = "0.3.2" tokio = { version = "1.17.0", features = ["sync", "rt"] } +tokio-stream = { version = "0.1.8", features = ["sync"] } webbrowser = "0.6.0" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index c5a1359..743990e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -70,6 +70,14 @@ fn run(mut args: Arguments) -> Result<()> { let filename = args.free_from_os_str::(|s| Ok(s.into()))?; + let mut watcher = FileWatcher::builder(); + + if watch_mode { + watcher.add_file(&filename); + } + + let watcher = watcher.build()?; + if !run_browser { let mut uw8 = MicroW8::new()?; @@ -77,14 +85,6 @@ fn run(mut args: Arguments) -> Result<()> { uw8.set_timeout(timeout); } - let mut watcher = FileWatcher::builder(); - - if watch_mode { - watcher.add_file(&filename); - } - - let watcher = watcher.build()?; - if let Err(err) = start_cart(&filename, &mut uw8, &config) { eprintln!("Load error: {}", err); if !watch_mode { @@ -119,6 +119,14 @@ fn run(mut args: Arguments) -> Result<()> { } loop { + if watcher.poll_changed_file()?.is_some() { + match load_cart(&filename, &config) { + Ok(cart) => server.load_module(&cart)?, + Err(err) => { + eprintln!("Load error: {}", err); + } + } + } std::thread::sleep(Duration::from_millis(100)); } } diff --git a/src/run_web.rs b/src/run_web.rs index 4144930..c029dbe 100644 --- a/src/run_web.rs +++ b/src/run_web.rs @@ -1,14 +1,28 @@ use anyhow::Result; -use std::thread; +use std::{ + sync::{Arc, Mutex}, + thread, +}; +use tokio::sync::broadcast; +use tokio_stream::{wrappers::BroadcastStream, Stream, StreamExt}; use warp::{http::Response, Filter}; -pub struct RunWebServer {} +pub struct RunWebServer { + cart: Arc>>, + tx: broadcast::Sender<()>, +} impl RunWebServer { pub fn new() -> RunWebServer { + let cart = Arc::new(Mutex::new(Vec::new())); + let (tx, _) = broadcast::channel(1); + + let server_cart = cart.clone(); + let server_tx = tx.clone(); thread::spawn(move || { let rt = tokio::runtime::Builder::new_current_thread() .enable_io() + .enable_time() .build() .expect("Failed to create tokio runtime"); rt.block_on(async { @@ -18,17 +32,37 @@ impl RunWebServer { .body(include_str!("run-web.html")) }); - let server_future = warp::serve(html).bind(([127, 0, 0, 1], 3030)); + let cart = warp::path("cart") + .map(move || server_cart.lock().map_or(Vec::new(), |c| c.clone())); + + let events = warp::path("events").and(warp::get()).map(move || { + fn event_stream( + tx: &broadcast::Sender<()>, + ) -> impl Stream> + { + BroadcastStream::new(tx.subscribe()) + .map(|_| Ok(warp::sse::Event::default().data("L"))) + } + warp::sse::reply(warp::sse::keep_alive().stream(event_stream(&server_tx))) + }); + + let server_future = + warp::serve(html.or(cart).or(events)).bind(([127, 0, 0, 1], 3030)); println!("Point browser at 127.0.0.1:3030"); - let _ = webbrowser::open("http://127.0.0.1:3030"); + let _ignore_result = webbrowser::open("http://127.0.0.1:3030"); server_future.await }); }); - RunWebServer {} + RunWebServer { cart, tx } } pub fn load_module(&mut self, module_data: &[u8]) -> Result<()> { + if let Ok(mut lock) = self.cart.lock() { + lock.clear(); + lock.extend_from_slice(module_data); + } + let _ignore_result = self.tx.send(()); Ok(()) } } diff --git a/web/src/microw8.js b/web/src/microw8.js index 1b4556f..26f347c 100644 --- a/web/src/microw8.js +++ b/web/src/microw8.js @@ -2,6 +2,13 @@ import loaderUrl from "data-url:../../platform/bin/loader.wasm"; import platformUrl from "data-url:../../platform/bin/platform.uw8"; export default function MicroW8(screen, config = {}) { + if(!config.setMessage) { + config.setMessage = (s, e) => { + if(e) { + console.log('error: ' + e); + } + } + } let canvasCtx = screen.getContext('2d'); let imageData = canvasCtx.createImageData(320, 240); diff --git a/web/src/run-web.js b/web/src/run-web.js index f93a4f3..89817fd 100644 --- a/web/src/run-web.js +++ b/web/src/run-web.js @@ -5,6 +5,7 @@ let events = new EventSource('events'); events.onmessage = event => { console.log(event.data); if(event.data == 'L') { - uw8.runModuleFromURL('cart'); + uw8.runModuleFromURL('cart', true); } -}; \ No newline at end of file +}; +uw8.runModuleFromURL('cart', true);