From 44b8656f2938eaf49780300c3e74288d73a961cd Mon Sep 17 00:00:00 2001 From: Dennis Ranke Date: Mon, 21 Feb 2022 22:23:52 +0100 Subject: [PATCH] make both native and browser runtime optional --- Cargo.toml | 15 ++- src/lib.rs | 278 +++------------------------------------------- src/main.rs | 107 +++++++++--------- src/run_native.rs | 260 +++++++++++++++++++++++++++++++++++++++++++ src/run_web.rs | 31 +++++- test.cwa | 13 +++ test.wasm | Bin 0 -> 129 bytes 7 files changed, 374 insertions(+), 330 deletions(-) create mode 100644 src/run_native.rs create mode 100644 test.cwa create mode 100644 test.wasm diff --git a/Cargo.toml b/Cargo.toml index 3672d07..4195385 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,8 +5,13 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = ["native", "browser"] +native = ["wasmtime"] +browser = ["warp", "tokio", "tokio-stream", "webbrowser"] + [dependencies] -wasmtime = "0.30" +wasmtime = { version = "0.30", optional = true } anyhow = "1" minifb = { version = "0.20", default-features = false, features = ["x11"] } notify = "4" @@ -15,7 +20,7 @@ curlywas = { git = "https://github.com/exoticorn/curlywas.git", rev = "196719b" wat = "1" 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 +warp = { version = "0.3.2", optional = true } +tokio = { version = "1.17.0", features = ["sync", "rt"], optional = true } +tokio-stream = { version = "0.1.8", features = ["sync"], optional = true } +webbrowser = { version = "0.6.0", optional = true } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index cd1816f..dc9c18c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,272 +1,22 @@ -use std::io::prelude::*; -use std::path::Path; -use std::sync::{Arc, Mutex}; -use std::time::Duration; -use std::{fs::File, thread, time::Instant}; - -use anyhow::Result; -use minifb::{Key, Window, WindowOptions}; -use wasmtime::{ - Engine, GlobalType, Memory, MemoryType, Module, Mutability, Store, TypedFunc, ValType, -}; - mod filewatcher; +#[cfg(feature = "native")] +mod run_native; +#[cfg(feature = "browser")] mod run_web; pub use filewatcher::FileWatcher; +#[cfg(feature = "native")] +pub use run_native::MicroW8; +#[cfg(feature = "browser")] pub use run_web::RunWebServer; -static GAMEPAD_KEYS: &'static [Key] = &[ - Key::Up, - Key::Down, - Key::Left, - Key::Right, - Key::Z, - Key::X, - Key::A, - Key::S, -]; +use anyhow::Result; -pub struct MicroW8 { - engine: Engine, - loader_module: Module, - window: Window, - window_buffer: Vec, - instance: Option, - timeout: u32, -} - -struct UW8Instance { - store: Store<()>, - memory: Memory, - end_frame: TypedFunc<(), ()>, - update: TypedFunc<(), ()>, - start_time: Instant, - module: Vec, - watchdog: Arc>, -} - -impl Drop for UW8Instance { - fn drop(&mut self) { - if let Ok(mut watchdog) = self.watchdog.lock() { - watchdog.stop = true; - } +pub trait Runtime { + fn is_open(&self) -> bool; + fn set_timeout(&mut self, _timeout: u32) { + eprintln!("Warning: runtime doesn't support timeout"); } -} - -struct UW8WatchDog { - interupt: wasmtime::InterruptHandle, - timeout: u32, - stop: bool, -} - -impl MicroW8 { - pub fn new() -> Result { - let engine = wasmtime::Engine::new(wasmtime::Config::new().interruptable(true))?; - - let loader_module = - wasmtime::Module::new(&engine, include_bytes!("../platform/bin/loader.wasm"))?; - - let mut options = WindowOptions::default(); - options.scale = minifb::Scale::X2; - options.scale_mode = minifb::ScaleMode::AspectRatioStretch; - options.resize = true; - let mut window = Window::new("MicroW8", 320, 240, options)?; - window.limit_update_rate(Some(std::time::Duration::from_micros(16666))); - - Ok(MicroW8 { - engine, - loader_module, - window, - window_buffer: vec![0u32; 320 * 240], - instance: None, - timeout: 30, - }) - } - - pub fn is_open(&self) -> bool { - self.window.is_open() && !self.window.is_key_down(Key::Escape) - } - - pub fn set_timeout(&mut self, timeout: u32) { - self.timeout = timeout; - } - - fn reset(&mut self) { - self.instance = None; - for v in &mut self.window_buffer { - *v = 0; - } - } - - pub fn load_from_file>(&mut self, path: P) -> Result<()> { - self.reset(); - - let mut module = vec![]; - File::open(path)?.read_to_end(&mut module)?; - self.load_from_memory(&module) - } - - pub fn load_from_memory(&mut self, module_data: &[u8]) -> Result<()> { - self.reset(); - - let mut store = wasmtime::Store::new(&self.engine, ()); - - let memory = wasmtime::Memory::new(&mut store, MemoryType::new(4, Some(4)))?; - - let mut linker = wasmtime::Linker::new(&self.engine); - linker.define("env", "memory", memory.clone())?; - - let loader_instance = linker.instantiate(&mut store, &self.loader_module)?; - let load_uw8 = loader_instance.get_typed_func::(&mut store, "load_uw8")?; - - let platform_data = include_bytes!("../platform/bin/platform.uw8"); - memory.data_mut(&mut store)[..platform_data.len()].copy_from_slice(platform_data); - let platform_length = - load_uw8.call(&mut store, platform_data.len() as i32)? as u32 as usize; - let platform_module = - wasmtime::Module::new(&self.engine, &memory.data(&store)[..platform_length])?; - - memory.data_mut(&mut store)[..module_data.len()].copy_from_slice(module_data); - let module_length = load_uw8.call(&mut store, module_data.len() as i32)? as u32 as usize; - let module = wasmtime::Module::new(&self.engine, &memory.data(&store)[..module_length])?; - - linker.func_wrap("env", "acos", |v: f32| v.acos())?; - linker.func_wrap("env", "asin", |v: f32| v.asin())?; - linker.func_wrap("env", "atan", |v: f32| v.atan())?; - linker.func_wrap("env", "atan2", |x: f32, y: f32| x.atan2(y))?; - linker.func_wrap("env", "cos", |v: f32| v.cos())?; - linker.func_wrap("env", "exp", |v: f32| v.exp())?; - linker.func_wrap("env", "log", |v: f32| v.ln())?; - linker.func_wrap("env", "sin", |v: f32| v.sin())?; - linker.func_wrap("env", "tan", |v: f32| v.tan())?; - linker.func_wrap("env", "pow", |a: f32, b: f32| a.powf(b))?; - for i in 9..64 { - linker.func_wrap("env", &format!("reserved{}", i), || {})?; - } - for i in 0..16 { - linker.define( - "env", - &format!("g_reserved{}", i), - wasmtime::Global::new( - &mut store, - GlobalType::new(ValType::I32, Mutability::Const), - 0.into(), - )?, - )?; - } - - let platform_instance = linker.instantiate(&mut store, &platform_module)?; - - for export in platform_instance.exports(&mut store) { - linker.define( - "env", - export.name(), - export - .into_func() - .expect("platform surely only exports functions"), - )?; - } - - let watchdog = Arc::new(Mutex::new(UW8WatchDog { - interupt: store.interrupt_handle()?, - timeout: self.timeout, - stop: false, - })); - - { - let watchdog = watchdog.clone(); - thread::spawn(move || loop { - thread::sleep(Duration::from_millis(17)); - if let Ok(mut watchdog) = watchdog.lock() { - if watchdog.stop { - break; - } - if watchdog.timeout > 0 { - watchdog.timeout -= 1; - if watchdog.timeout == 0 { - watchdog.interupt.interrupt(); - } - } - } else { - break; - } - }); - } - - let instance = linker.instantiate(&mut store, &module)?; - if let Ok(mut watchdog) = watchdog.lock() { - watchdog.timeout = 0; - } - let end_frame = platform_instance.get_typed_func::<(), (), _>(&mut store, "endFrame")?; - let update = instance.get_typed_func::<(), (), _>(&mut store, "upd")?; - - self.instance = Some(UW8Instance { - store, - memory, - end_frame, - update, - start_time: Instant::now(), - module: module_data.into(), - watchdog, - }); - - Ok(()) - } - - pub fn run_frame(&mut self) -> Result<()> { - let mut result = Ok(()); - if let Some(mut instance) = self.instance.take() { - { - let time = instance.start_time.elapsed().as_millis() as i32; - let mut gamepad: u32 = 0; - for key in self.window.get_keys() { - if let Some(index) = GAMEPAD_KEYS - .iter() - .enumerate() - .find(|(_, &k)| k == key) - .map(|(i, _)| i) - { - gamepad |= 1 << index; - } - } - - let mem = instance.memory.data_mut(&mut instance.store); - mem[64..68].copy_from_slice(&time.to_le_bytes()); - mem[68..72].copy_from_slice(&gamepad.to_le_bytes()); - } - - if let Ok(mut watchdog) = instance.watchdog.lock() { - watchdog.timeout = self.timeout; - } - result = instance.update.call(&mut instance.store, ()); - if let Ok(mut watchdog) = instance.watchdog.lock() { - watchdog.timeout = 0; - } - instance.end_frame.call(&mut instance.store, ())?; - - let memory = instance.memory.data(&instance.store); - let framebuffer = &memory[120..]; - let palette = &memory[0x13000..]; - for i in 0..320 * 240 { - let offset = framebuffer[i] as usize * 4; - self.window_buffer[i] = 0xff000000 - | ((palette[offset + 0] as u32) << 16) - | ((palette[offset + 1] as u32) << 8) - | palette[offset + 2] as u32; - } - - if self.window.is_key_pressed(Key::R, minifb::KeyRepeat::No) { - self.load_from_memory(&instance.module)?; - } else if result.is_ok() { - self.instance = Some(instance); - } - } - - self.window - .update_with_buffer(&self.window_buffer, 320, 240)?; - - result?; - Ok(()) - } -} + fn load(&mut self, module_data: &[u8]) -> Result<()>; + fn run_frame(&mut self) -> Result<()>; +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 743990e..3a6e185 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,24 +1,26 @@ use std::fs::File; use std::io::prelude::*; +use std::path::{Path, PathBuf}; use std::process; -use std::time::Duration; -use std::{ - path::{Path, PathBuf}, - process::exit, -}; use anyhow::Result; use pico_args::Arguments; -use uw8::{FileWatcher, MicroW8, RunWebServer}; +#[cfg(feature = "native")] +use uw8::MicroW8; +#[cfg(feature = "browser")] +use uw8::RunWebServer; +#[cfg(any(feature = "native", feature = "browser"))] +use uw8::Runtime; fn main() -> Result<()> { let mut args = Arguments::from_env(); - match args.subcommand()?.as_ref().map(|s| s.as_str()) { + match args.subcommand()?.as_deref() { Some("version") => { println!("{}", env!("CARGO_PKG_VERSION")); Ok(()) } + #[cfg(any(feature = "native", feature = "browser"))] Some("run") => run(args), Some("pack") => pack(args), Some("unpack") => unpack(args), @@ -28,7 +30,8 @@ fn main() -> Result<()> { println!("uw8 {}", env!("CARGO_PKG_VERSION")); println!(); println!("Usage:"); - println!(" uw8 run [-t/--timeout ] [-w/--watch] [-p/--pack] [-u/--uncompressed] [-l/--level] [-o/--output ] "); + #[cfg(any(feature = "native", feature = "browser"))] + println!(" uw8 run [-t/--timeout ] [--b/--browser] [-w/--watch] [-p/--pack] [-u/--uncompressed] [-l/--level] [-o/--output ] "); println!(" uw8 pack [-u/--uncompressed] [-l/--level] "); println!(" uw8 unpack "); println!(" uw8 compile [-d/--debug] "); @@ -42,6 +45,7 @@ fn main() -> Result<()> { } } +#[cfg(any(feature = "native", feature = "browser"))] fn run(mut args: Arguments) -> Result<()> { let watch_mode = args.contains(["-w", "--watch"]); let timeout: Option = args.opt_value_from_str(["-t", "--timeout"])?; @@ -66,11 +70,14 @@ fn run(mut args: Arguments) -> Result<()> { config.output_path = Some(path); } + #[cfg(feature = "native")] let run_browser = args.contains(["-b", "--browser"]); + #[cfg(not(feature = "native"))] + let run_browser = args.contains(["-b", "--browser"]) || true; let filename = args.free_from_os_str::(|s| Ok(s.into()))?; - let mut watcher = FileWatcher::builder(); + let mut watcher = uw8::FileWatcher::builder(); if watch_mode { watcher.add_file(&filename); @@ -78,57 +85,44 @@ fn run(mut args: Arguments) -> Result<()> { let watcher = watcher.build()?; - if !run_browser { - let mut uw8 = MicroW8::new()?; + use std::process::exit; - if let Some(timeout) = timeout { - uw8.set_timeout(timeout); + let mut runtime: Box = if !run_browser { + #[cfg(not(feature = "native"))] + unimplemented!(); + #[cfg(feature = "native")] + Box::new(MicroW8::new()?) + } else { + #[cfg(not(feature = "browser"))] + unimplemented!(); + #[cfg(feature = "browser")] + Box::new(RunWebServer::new()) + }; + + if let Some(timeout) = timeout { + runtime.set_timeout(timeout); + } + + if let Err(err) = start_cart(&filename, &mut *runtime, &config) { + eprintln!("Load error: {}", err); + if !watch_mode { + exit(1); + } + } + + while runtime.is_open() { + if watcher.poll_changed_file()?.is_some() { + if let Err(err) = start_cart(&filename, &mut *runtime, &config) { + eprintln!("Load error: {}", err); + } } - if let Err(err) = start_cart(&filename, &mut uw8, &config) { - eprintln!("Load error: {}", err); + if let Err(err) = runtime.run_frame() { + eprintln!("Runtime error: {}", err); if !watch_mode { exit(1); } } - - while uw8.is_open() { - if watcher.poll_changed_file()?.is_some() { - if let Err(err) = start_cart(&filename, &mut uw8, &config) { - eprintln!("Load error: {}", err); - } - } - - if let Err(err) = uw8.run_frame() { - eprintln!("Runtime error: {}", err); - if !watch_mode { - exit(1); - } - } - } - } else { - let mut server = RunWebServer::new(); - match load_cart(&filename, &config) { - Ok(cart) => server.load_module(&cart)?, - Err(err) => { - eprintln!("Load error: {}", err); - if !watch_mode { - exit(1); - } - } - } - - 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)); - } } Ok(()) @@ -165,10 +159,11 @@ fn load_cart(filename: &Path, config: &Config) -> Result> { Ok(cart) } -fn start_cart(filename: &Path, uw8: &mut MicroW8, config: &Config) -> Result<()> { +#[cfg(any(feature = "native", feature = "browser"))] +fn start_cart(filename: &Path, runtime: &mut dyn Runtime, config: &Config) -> Result<()> { let cart = load_cart(filename, config)?; - if let Err(err) = uw8.load_from_memory(&cart) { + if let Err(err) = runtime.load(&cart) { eprintln!("Load error: {}", err); Err(err) } else { @@ -208,7 +203,7 @@ fn unpack(mut args: Arguments) -> Result<()> { let in_file = args.free_from_os_str::(|s| Ok(s.into()))?; let out_file = args.free_from_os_str::(|s| Ok(s.into()))?; - uw8_tool::unpack_file(&in_file, &out_file).into() + uw8_tool::unpack_file(&in_file, &out_file) } fn compile(mut args: Arguments) -> Result<()> { diff --git a/src/run_native.rs b/src/run_native.rs new file mode 100644 index 0000000..d310aa8 --- /dev/null +++ b/src/run_native.rs @@ -0,0 +1,260 @@ +use std::sync::{Arc, Mutex}; +use std::time::Duration; +use std::{thread, time::Instant}; + +use anyhow::Result; +use minifb::{Key, Window, WindowOptions}; +use wasmtime::{ + Engine, GlobalType, Memory, MemoryType, Module, Mutability, Store, TypedFunc, ValType, +}; + +static GAMEPAD_KEYS: &[Key] = &[ + Key::Up, + Key::Down, + Key::Left, + Key::Right, + Key::Z, + Key::X, + Key::A, + Key::S, +]; + +pub struct MicroW8 { + engine: Engine, + loader_module: Module, + window: Window, + window_buffer: Vec, + instance: Option, + timeout: u32, +} + +struct UW8Instance { + store: Store<()>, + memory: Memory, + end_frame: TypedFunc<(), ()>, + update: TypedFunc<(), ()>, + start_time: Instant, + module: Vec, + watchdog: Arc>, +} + +impl Drop for UW8Instance { + fn drop(&mut self) { + if let Ok(mut watchdog) = self.watchdog.lock() { + watchdog.stop = true; + } + } +} + +struct UW8WatchDog { + interupt: wasmtime::InterruptHandle, + timeout: u32, + stop: bool, +} + +impl MicroW8 { + pub fn new() -> Result { + let engine = wasmtime::Engine::new(wasmtime::Config::new().interruptable(true))?; + + let loader_module = + wasmtime::Module::new(&engine, include_bytes!("../platform/bin/loader.wasm"))?; + + let options = WindowOptions { + scale: minifb::Scale::X2, + scale_mode: minifb::ScaleMode::AspectRatioStretch, + resize: true, + ..Default::default() + }; + let mut window = Window::new("MicroW8", 320, 240, options)?; + window.limit_update_rate(Some(std::time::Duration::from_micros(16666))); + + Ok(MicroW8 { + engine, + loader_module, + window, + window_buffer: vec![0u32; 320 * 240], + instance: None, + timeout: 30, + }) + } + + fn reset(&mut self) { + self.instance = None; + for v in &mut self.window_buffer { + *v = 0; + } + } +} + +impl super::Runtime for MicroW8 { + fn is_open(&self) -> bool { + self.window.is_open() && !self.window.is_key_down(Key::Escape) + } + + fn set_timeout(&mut self, timeout: u32) { + self.timeout = timeout; + } + + fn load(&mut self, module_data: &[u8]) -> Result<()> { + self.reset(); + + let mut store = wasmtime::Store::new(&self.engine, ()); + + let memory = wasmtime::Memory::new(&mut store, MemoryType::new(4, Some(4)))?; + + let mut linker = wasmtime::Linker::new(&self.engine); + linker.define("env", "memory", memory)?; + + let loader_instance = linker.instantiate(&mut store, &self.loader_module)?; + let load_uw8 = loader_instance.get_typed_func::(&mut store, "load_uw8")?; + + let platform_data = include_bytes!("../platform/bin/platform.uw8"); + memory.data_mut(&mut store)[..platform_data.len()].copy_from_slice(platform_data); + let platform_length = + load_uw8.call(&mut store, platform_data.len() as i32)? as u32 as usize; + let platform_module = + wasmtime::Module::new(&self.engine, &memory.data(&store)[..platform_length])?; + + memory.data_mut(&mut store)[..module_data.len()].copy_from_slice(module_data); + let module_length = load_uw8.call(&mut store, module_data.len() as i32)? as u32 as usize; + let module = wasmtime::Module::new(&self.engine, &memory.data(&store)[..module_length])?; + + linker.func_wrap("env", "acos", |v: f32| v.acos())?; + linker.func_wrap("env", "asin", |v: f32| v.asin())?; + linker.func_wrap("env", "atan", |v: f32| v.atan())?; + linker.func_wrap("env", "atan2", |x: f32, y: f32| x.atan2(y))?; + linker.func_wrap("env", "cos", |v: f32| v.cos())?; + linker.func_wrap("env", "exp", |v: f32| v.exp())?; + linker.func_wrap("env", "log", |v: f32| v.ln())?; + linker.func_wrap("env", "sin", |v: f32| v.sin())?; + linker.func_wrap("env", "tan", |v: f32| v.tan())?; + linker.func_wrap("env", "pow", |a: f32, b: f32| a.powf(b))?; + for i in 9..64 { + linker.func_wrap("env", &format!("reserved{}", i), || {})?; + } + for i in 0..16 { + linker.define( + "env", + &format!("g_reserved{}", i), + wasmtime::Global::new( + &mut store, + GlobalType::new(ValType::I32, Mutability::Const), + 0.into(), + )?, + )?; + } + + let platform_instance = linker.instantiate(&mut store, &platform_module)?; + + for export in platform_instance.exports(&mut store) { + linker.define( + "env", + export.name(), + export + .into_func() + .expect("platform surely only exports functions"), + )?; + } + + let watchdog = Arc::new(Mutex::new(UW8WatchDog { + interupt: store.interrupt_handle()?, + timeout: self.timeout, + stop: false, + })); + + { + let watchdog = watchdog.clone(); + thread::spawn(move || loop { + thread::sleep(Duration::from_millis(17)); + if let Ok(mut watchdog) = watchdog.lock() { + if watchdog.stop { + break; + } + if watchdog.timeout > 0 { + watchdog.timeout -= 1; + if watchdog.timeout == 0 { + watchdog.interupt.interrupt(); + } + } + } else { + break; + } + }); + } + + let instance = linker.instantiate(&mut store, &module)?; + if let Ok(mut watchdog) = watchdog.lock() { + watchdog.timeout = 0; + } + let end_frame = platform_instance.get_typed_func::<(), (), _>(&mut store, "endFrame")?; + let update = instance.get_typed_func::<(), (), _>(&mut store, "upd")?; + + self.instance = Some(UW8Instance { + store, + memory, + end_frame, + update, + start_time: Instant::now(), + module: module_data.into(), + watchdog, + }); + + Ok(()) + } + + fn run_frame(&mut self) -> Result<()> { + let mut result = Ok(()); + if let Some(mut instance) = self.instance.take() { + { + let time = instance.start_time.elapsed().as_millis() as i32; + let mut gamepad: u32 = 0; + for key in self.window.get_keys() { + if let Some(index) = GAMEPAD_KEYS + .iter() + .enumerate() + .find(|(_, &k)| k == key) + .map(|(i, _)| i) + { + gamepad |= 1 << index; + } + } + + let mem = instance.memory.data_mut(&mut instance.store); + mem[64..68].copy_from_slice(&time.to_le_bytes()); + mem[68..72].copy_from_slice(&gamepad.to_le_bytes()); + } + + if let Ok(mut watchdog) = instance.watchdog.lock() { + watchdog.timeout = self.timeout; + } + result = instance.update.call(&mut instance.store, ()); + if let Ok(mut watchdog) = instance.watchdog.lock() { + watchdog.timeout = 0; + } + instance.end_frame.call(&mut instance.store, ())?; + + let memory = instance.memory.data(&instance.store); + let framebuffer = &memory[120..(120 + 320 * 240)]; + let palette = &memory[0x13000..]; + for (i, &color_index) in framebuffer.iter().enumerate() { + let offset = color_index as usize * 4; + self.window_buffer[i] = 0xff000000 + | ((palette[offset] as u32) << 16) + | ((palette[offset + 1] as u32) << 8) + | palette[offset + 2] as u32; + } + + if self.window.is_key_pressed(Key::R, minifb::KeyRepeat::No) { + self.load(&instance.module)?; + } else if result.is_ok() { + self.instance = Some(instance); + } + } + + self.window + .update_with_buffer(&self.window_buffer, 320, 240)?; + + result?; + Ok(()) + } +} diff --git a/src/run_web.rs b/src/run_web.rs index c029dbe..4eede6b 100644 --- a/src/run_web.rs +++ b/src/run_web.rs @@ -1,5 +1,6 @@ use anyhow::Result; use std::{ + net::SocketAddr, sync::{Arc, Mutex}, thread, }; @@ -46,18 +47,23 @@ impl RunWebServer { 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 _ignore_result = webbrowser::open("http://127.0.0.1:3030"); + let socket_addr = "127.0.0.1:3030" + .parse::() + .expect("Failed to parse socket address"); + + let server_future = warp::serve(html.or(cart).or(events)).bind(socket_addr); + println!("Point browser at http://{}", socket_addr); + let _ignore_result = webbrowser::open(&format!("http://{}", socket_addr)); server_future.await }); }); RunWebServer { cart, tx } } +} - pub fn load_module(&mut self, module_data: &[u8]) -> Result<()> { +impl super::Runtime for RunWebServer { + fn load(&mut self, module_data: &[u8]) -> Result<()> { if let Ok(mut lock) = self.cart.lock() { lock.clear(); lock.extend_from_slice(module_data); @@ -65,4 +71,19 @@ impl RunWebServer { let _ignore_result = self.tx.send(()); Ok(()) } + + fn is_open(&self) -> bool { + true + } + + fn run_frame(&mut self) -> Result<()> { + std::thread::sleep(std::time::Duration::from_millis(100)); + Ok(()) + } } + +impl Default for RunWebServer { + fn default() -> RunWebServer { + RunWebServer::new() + } +} \ No newline at end of file diff --git a/test.cwa b/test.cwa new file mode 100644 index 0000000..6e5984b --- /dev/null +++ b/test.cwa @@ -0,0 +1,13 @@ +import "env.memory" memory(4); +import "env.printString" fn print(i32); + +export fn upd() { +} + +start fn start() { + print(0); +} + +data 0 { + "Press " i8(0xe0) " and " i8(0xe1) " to adjust, " i8(0xcc) " to commit." i8(0) +} \ No newline at end of file diff --git a/test.wasm b/test.wasm new file mode 100644 index 0000000000000000000000000000000000000000..6d572c65aa077f8231f90313425a848d0b73d4c0 GIT binary patch literal 129 zcmWlQK?=e!5Jmq?q!nl55yp+H;vKw!-~}2?kP1mkk_xW8poh?#m~I~LEgozM1c0_Y z(i