From 9e6ea954c64e96ea0a522269e4e52e31db6c4dd2 Mon Sep 17 00:00:00 2001 From: Fredrik Fornwall Date: Tue, 27 Jun 2023 10:44:54 +0200 Subject: [PATCH] Extract web.rs --- src/event_loop.rs | 18 ++-- src/lib.rs | 206 ++-------------------------------------------- src/web.rs | 179 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 195 insertions(+), 208 deletions(-) create mode 100644 src/web.rs diff --git a/src/event_loop.rs b/src/event_loop.rs index 76910a9..a6492b8 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -4,14 +4,16 @@ use winit::{ }; #[cfg(target_arch = "wasm32")] -use crate::CustomWinitEvent; +use crate::web::CustomWinitEvent; use crate::State; -pub fn handle_event_loop( - event: &crate::EventTypeUsed, - state: &mut State, - control_flow: &mut ControlFlow, -) { +#[cfg(target_arch = "wasm32")] +type EventTypeUsed<'a> = crate::web::EventTypeUsed<'a>; + +#[cfg(not(target_arch = "wasm32"))] +type EventTypeUsed<'a> = winit::event::Event<'a, ()>; + +pub fn handle_event_loop(event: &EventTypeUsed, state: &mut State, control_flow: &mut ControlFlow) { match event { #[cfg(target_arch = "wasm32")] Event::UserEvent(event) => match event { @@ -94,7 +96,7 @@ pub fn handle_event_loop( if c == "f" || c == "F" { #[cfg(target_arch = "wasm32")] { - crate::toggle_fullscreen(); + crate::web::toggle_fullscreen(); } #[cfg(not(target_arch = "wasm32"))] { @@ -108,7 +110,7 @@ pub fn handle_event_loop( } } else if c == "h" || c == "H" { #[cfg(target_arch = "wasm32")] - crate::toggle_controls(); + crate::web::toggle_controls(); } else if c == "r" || c == "R" { state.reset(); } else if c == "q" || c == "Q" { diff --git a/src/lib.rs b/src/lib.rs index 77e31a0..5a84ce6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,43 +1,13 @@ mod event_loop; mod rules; - #[cfg(target_arch = "wasm32")] -use std::sync::Mutex; +mod web; use rand::prelude::*; use rand_chacha::ChaCha20Rng; -#[cfg(target_arch = "wasm32")] -use wasm_bindgen::prelude::*; use wgpu::util::DeviceExt; -#[cfg(target_arch = "wasm32")] -use winit::event_loop::EventLoopProxy; -use winit::{ - dpi::PhysicalPosition, - event::Event, - window::{Window, WindowBuilder}, -}; - -#[cfg(target_arch = "wasm32")] -#[derive(Debug, Clone, Copy)] -pub enum CustomWinitEvent { - RuleChange(u32), - SizeChange(u32), - SetDensity(u8), - SetGenerationsPerSecond(u8), - Reset, - TogglePause, -} - -#[cfg(target_arch = "wasm32")] -type EventTypeUsed<'a> = Event<'a, CustomWinitEvent>; -#[cfg(not(target_arch = "wasm32"))] -type EventTypeUsed<'a> = Event<'a, ()>; - -#[cfg(target_arch = "wasm32")] -thread_local! { - pub static EVENT_LOOP_PROXY: Mutex>> = Mutex::new(None); -} +use winit::{dpi::PhysicalPosition, window::Window}; #[cfg(not(target_arch = "wasm32"))] pub async fn run() { @@ -45,7 +15,9 @@ pub async fn run() { let event_loop = winit::event_loop::EventLoop::new(); - let window = WindowBuilder::new().build(&event_loop).unwrap(); + let window = winit::window::WindowBuilder::new() + .build(&event_loop) + .unwrap(); let mut state = State::new(window, None, None, None, None, false, None) .await @@ -82,172 +54,6 @@ async fn android_run(app: winit::platform::android::activity::AndroidApp) { }); } -#[cfg(target_arch = "wasm32")] -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(js_name = setNewState)] - pub fn set_new_state( - rule_idx: u32, - cells_width: u32, - seed: u32, - density: u8, - paused: bool, - generations_per_second: u8, - frame: u64, - ); - - #[wasm_bindgen(js_name = toggleFullscreen)] - pub fn toggle_fullscreen(); - - #[wasm_bindgen(js_name = toggleControls)] - pub fn toggle_controls(); -} - -#[cfg(target_arch = "wasm32")] -#[wasm_bindgen(js_name = "setNewRule")] -pub fn set_new_rule(rule_idx: u32) { - EVENT_LOOP_PROXY.with(|proxy| { - if let Ok(unlocked) = proxy.lock() { - if let Some(event_loop_proxy) = &*unlocked { - event_loop_proxy - .send_event(CustomWinitEvent::RuleChange(rule_idx)) - .ok(); - } - } - }); -} - -#[cfg(target_arch = "wasm32")] -#[wasm_bindgen(js_name = "setNewSize")] -pub fn set_new_size(size: u32) { - EVENT_LOOP_PROXY.with(|proxy| { - if let Ok(unlocked) = proxy.lock() { - if let Some(event_loop_proxy) = &*unlocked { - event_loop_proxy - .send_event(CustomWinitEvent::SizeChange(size)) - .ok(); - } - } - }); -} - -#[cfg(target_arch = "wasm32")] -#[wasm_bindgen(js_name = "resetGame")] -pub fn reset_game() { - EVENT_LOOP_PROXY.with(|proxy| { - if let Ok(unlocked) = proxy.lock() { - if let Some(event_loop_proxy) = &*unlocked { - event_loop_proxy.send_event(CustomWinitEvent::Reset).ok(); - } - } - }); -} - -#[cfg(target_arch = "wasm32")] -#[wasm_bindgen(js_name = "togglePause")] -pub fn toggle_pause() { - EVENT_LOOP_PROXY.with(|proxy| { - if let Ok(unlocked) = proxy.lock() { - if let Some(event_loop_proxy) = &*unlocked { - event_loop_proxy - .send_event(CustomWinitEvent::TogglePause) - .ok(); - } - } - }); -} - -#[cfg(target_arch = "wasm32")] -#[wasm_bindgen(js_name = "setDensity")] -pub fn set_density(density: u8) { - EVENT_LOOP_PROXY.with(|proxy| { - if let Ok(unlocked) = proxy.lock() { - if let Some(event_loop_proxy) = &*unlocked { - event_loop_proxy - .send_event(CustomWinitEvent::SetDensity(density)) - .ok(); - } - } - }); -} - -#[cfg(target_arch = "wasm32")] -#[wasm_bindgen(js_name = "setGenerationsPerSecond")] -pub fn set_generations_per_second(generations_per_second: u8) { - EVENT_LOOP_PROXY.with(|proxy| { - if let Ok(unlocked) = proxy.lock() { - if let Some(event_loop_proxy) = &*unlocked { - event_loop_proxy - .send_event(CustomWinitEvent::SetGenerationsPerSecond( - generations_per_second, - )) - .ok(); - } - } - }); -} - -#[cfg(target_arch = "wasm32")] -#[wasm_bindgen] -pub async fn run( - rule_idx: Option, - size: Option, - seed: Option, - initial_density: Option, - paused: bool, - generations_per_second: Option, -) -> Result<(), String> { - use winit::event_loop::EventLoopBuilder; - use winit::platform::web::{EventLoopExtWebSys, WindowBuilderExtWebSys}; - - std::panic::set_hook(Box::new(console_error_panic_hook::hook)); - console_log::init_with_level(log::Level::Info) - .map_err(|e| format!("Couldn't initialize logger: {e}"))?; - - let event_loop = EventLoopBuilder::::with_user_event().build(); - - let event_loop_proxy = event_loop.create_proxy(); - EVENT_LOOP_PROXY.with(move |proxy| { - if let Ok(mut proxy) = proxy.lock() { - *proxy = Some(event_loop_proxy); - } - }); - - let canvas_element = web_sys::window() - .and_then(|win| win.document()) - .and_then(|doc| { - let canvas = doc.get_element_by_id("webgpu-canvas")?; - canvas.dyn_into::().ok() - }) - .ok_or("Could not get canvas element")?; - - let window = WindowBuilder::new() - .with_canvas(Some(canvas_element)) - .with_prevent_default(false) - .build(&event_loop) - .unwrap(); - - let mut state = State::new( - window, - rule_idx, - size, - seed, - initial_density, - paused, - generations_per_second, - ) - .await - .map_err(|()| "Failed to build".to_string())?; - - state.inform_ui_about_state(); - - event_loop.spawn(move |event, _, control_flow| { - event_loop::handle_event_loop(&event, &mut state, control_flow); - }); - - Ok(()) -} - pub struct State { generations_per_second: u8, paused: bool, @@ -920,7 +726,7 @@ impl State { )); #[cfg(target_arch = "wasm32")] - set_new_state( + web::set_new_state( self.rule_idx, self.cells_width, self.seed, diff --git a/src/web.rs b/src/web.rs new file mode 100644 index 0000000..a15e3b1 --- /dev/null +++ b/src/web.rs @@ -0,0 +1,179 @@ +use std::sync::Mutex; +use wasm_bindgen::prelude::*; +use winit::event_loop::EventLoopProxy; +use winit::{event::Event, window::WindowBuilder}; + +#[derive(Debug, Clone, Copy)] +pub enum CustomWinitEvent { + RuleChange(u32), + SizeChange(u32), + SetDensity(u8), + SetGenerationsPerSecond(u8), + Reset, + TogglePause, +} + +pub(crate) type EventTypeUsed<'a> = Event<'a, CustomWinitEvent>; + +thread_local! { + pub static EVENT_LOOP_PROXY: Mutex>> = Mutex::new(None); +} + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(js_name = setNewState)] + pub fn set_new_state( + rule_idx: u32, + cells_width: u32, + seed: u32, + density: u8, + paused: bool, + generations_per_second: u8, + frame: u64, + ); + + #[wasm_bindgen(js_name = toggleFullscreen)] + pub fn toggle_fullscreen(); + + #[wasm_bindgen(js_name = toggleControls)] + pub fn toggle_controls(); +} + +#[wasm_bindgen(js_name = "setNewRule")] +pub fn set_new_rule(rule_idx: u32) { + EVENT_LOOP_PROXY.with(|proxy| { + if let Ok(unlocked) = proxy.lock() { + if let Some(event_loop_proxy) = &*unlocked { + event_loop_proxy + .send_event(CustomWinitEvent::RuleChange(rule_idx)) + .ok(); + } + } + }); +} + +#[cfg(target_arch = "wasm32")] +#[wasm_bindgen(js_name = "setNewSize")] +pub fn set_new_size(size: u32) { + EVENT_LOOP_PROXY.with(|proxy| { + if let Ok(unlocked) = proxy.lock() { + if let Some(event_loop_proxy) = &*unlocked { + event_loop_proxy + .send_event(CustomWinitEvent::SizeChange(size)) + .ok(); + } + } + }); +} + +#[wasm_bindgen(js_name = "resetGame")] +pub fn reset_game() { + EVENT_LOOP_PROXY.with(|proxy| { + if let Ok(unlocked) = proxy.lock() { + if let Some(event_loop_proxy) = &*unlocked { + event_loop_proxy.send_event(CustomWinitEvent::Reset).ok(); + } + } + }); +} + +#[wasm_bindgen(js_name = "togglePause")] +pub fn toggle_pause() { + EVENT_LOOP_PROXY.with(|proxy| { + if let Ok(unlocked) = proxy.lock() { + if let Some(event_loop_proxy) = &*unlocked { + event_loop_proxy + .send_event(CustomWinitEvent::TogglePause) + .ok(); + } + } + }); +} + +#[wasm_bindgen(js_name = "setDensity")] +pub fn set_density(density: u8) { + EVENT_LOOP_PROXY.with(|proxy| { + if let Ok(unlocked) = proxy.lock() { + if let Some(event_loop_proxy) = &*unlocked { + event_loop_proxy + .send_event(CustomWinitEvent::SetDensity(density)) + .ok(); + } + } + }); +} + +#[wasm_bindgen(js_name = "setGenerationsPerSecond")] +pub fn set_generations_per_second(generations_per_second: u8) { + EVENT_LOOP_PROXY.with(|proxy| { + if let Ok(unlocked) = proxy.lock() { + if let Some(event_loop_proxy) = &*unlocked { + event_loop_proxy + .send_event(CustomWinitEvent::SetGenerationsPerSecond( + generations_per_second, + )) + .ok(); + } + } + }); +} + +#[wasm_bindgen] +pub async fn run( + rule_idx: Option, + size: Option, + seed: Option, + initial_density: Option, + paused: bool, + generations_per_second: Option, +) -> Result<(), String> { + use winit::event_loop::EventLoopBuilder; + use winit::platform::web::{EventLoopExtWebSys, WindowBuilderExtWebSys}; + + std::panic::set_hook(Box::new(console_error_panic_hook::hook)); + console_log::init_with_level(log::Level::Info) + .map_err(|e| format!("Couldn't initialize logger: {e}"))?; + + let event_loop = EventLoopBuilder::::with_user_event().build(); + + let event_loop_proxy = event_loop.create_proxy(); + EVENT_LOOP_PROXY.with(move |proxy| { + if let Ok(mut proxy) = proxy.lock() { + *proxy = Some(event_loop_proxy); + } + }); + + let canvas_element = web_sys::window() + .and_then(|win| win.document()) + .and_then(|doc| { + let canvas = doc.get_element_by_id("webgpu-canvas")?; + canvas.dyn_into::().ok() + }) + .ok_or("Could not get canvas element")?; + + let window = WindowBuilder::new() + .with_canvas(Some(canvas_element)) + .with_prevent_default(false) + .build(&event_loop) + .unwrap(); + + let mut state = crate::State::new( + window, + rule_idx, + size, + seed, + initial_density, + paused, + generations_per_second, + ) + .await + .map_err(|()| "Failed to build".to_string())?; + + state.inform_ui_about_state(); + + event_loop.spawn(move |event, _, control_flow| { + crate::event_loop::handle_event_loop(&event, &mut state, control_flow); + }); + + Ok(()) +}