From d26e80ffaaebc609c0dbb68f918d8b79a4dedede Mon Sep 17 00:00:00 2001 From: transistor Date: Tue, 11 Oct 2022 14:40:12 -0700 Subject: [PATCH] Refactored pixels web frontend to separate sim run It will now run the sim on a separate loop powered by setTimeout instead of trying to do it inline with the frame updating --- .../pixels/assets/moa-genesis/index.html | 101 +-------- .../pixels/assets/moa-genesis/interface.js | 69 ++++++ .../pixels/assets/moa-genesis/style.css | 21 ++ .../frontends/pixels/src/bin/moa-genesis.rs | 8 + emulator/frontends/pixels/src/frontend.rs | 203 ++++++------------ emulator/frontends/pixels/src/lib.rs | 4 +- emulator/frontends/pixels/src/native.rs | 18 ++ emulator/frontends/pixels/src/settings.rs | 12 +- emulator/frontends/pixels/src/web.rs | 170 ++++++++++++++- 9 files changed, 358 insertions(+), 248 deletions(-) create mode 100644 emulator/frontends/pixels/assets/moa-genesis/interface.js create mode 100644 emulator/frontends/pixels/assets/moa-genesis/style.css diff --git a/emulator/frontends/pixels/assets/moa-genesis/index.html b/emulator/frontends/pixels/assets/moa-genesis/index.html index 2e72431..9737f31 100644 --- a/emulator/frontends/pixels/assets/moa-genesis/index.html +++ b/emulator/frontends/pixels/assets/moa-genesis/index.html @@ -3,37 +3,16 @@ - - Hello Pixels + Web + + Sega Genesis - Moa +
+
@@ -42,74 +21,8 @@ - +
+ +
diff --git a/emulator/frontends/pixels/assets/moa-genesis/interface.js b/emulator/frontends/pixels/assets/moa-genesis/interface.js new file mode 100644 index 0000000..ede3227 --- /dev/null +++ b/emulator/frontends/pixels/assets/moa-genesis/interface.js @@ -0,0 +1,69 @@ + +import * as Emulator from './moa-genesis.js'; + +var reader = new FileReader(); +reader.onloadend = function (e) { + var data = new Uint8Array(reader.result); + // If the SMD file magic number is present, then convert it before loading + if (data[8] == 0xAA && data[9] == 0xBB) + data = Emulator.smd_to_bin(data); + Emulator.set_rom_data(data); +}; + +var file_input = document.getElementById("rom-file"); +file_input.addEventListener("change", e => { + reader.readAsArrayBuffer(file_input.files[0]) +}); + +function initialize_emulator() { + let host = Emulator.new_host(); + let system = Emulator.load_system(host, Emulator.get_load_system_fn()); + + let last_update = performance.now(); + setTimeout(function refreshFrame() { + let current = performance.now(); + let diff = Math.min(current - last_update, 100); + let remaining = Math.max((16 * Emulator.get_speed()) - diff, 0); + //console.log(diff, remaining); + last_update = current; + + Emulator.run_system_for(system, diff * 1_000_000); + if (Emulator.is_running()) { + setTimeout(refreshFrame, remaining); + } + }, 0); + /* + setTimeout(function refreshFrame() { + let run_time = run_system_for(system, 66_000_000); + setTimeout(refreshFrame, 66 - run_time); + }, 0); + */ + + Emulator.host_run_loop(host); +} + +document.getElementById("reset").addEventListener("click", () => { + Emulator.request_stop(); + //start(); +}); + +document.getElementById("power").addEventListener("click", () => { + if (Emulator.is_running()) + Emulator.request_stop(); + else + initialize_emulator(); +}); + +document.getElementById("speed").addEventListener("change", (e) => { + Emulator.set_speed(e.target.value); +}); + +var file_input = document.getElementById("rom-file"); +var frame_rate_el = document.getElementById("frame-rate"); +var frame_rate = setInterval(function () { + frame_rate_el.value = Emulator.get_frames_since(); +}, 1000); + +window.addEventListener("load", () => { + Emulator.default(); +}); diff --git a/emulator/frontends/pixels/assets/moa-genesis/style.css b/emulator/frontends/pixels/assets/moa-genesis/style.css new file mode 100644 index 0000000..4ba1de8 --- /dev/null +++ b/emulator/frontends/pixels/assets/moa-genesis/style.css @@ -0,0 +1,21 @@ +body { + background-color: #000; + margin: 0; + overflow: hidden; +} + +#config { + background-color: #888; +} + +#metrics { + float: right; + color: #DDD; + background-color: #000; +} + +#metrics input { + width: 2em; + color: #DDD; + background-color: #000; +} diff --git a/emulator/frontends/pixels/src/bin/moa-genesis.rs b/emulator/frontends/pixels/src/bin/moa-genesis.rs index cb46a89..c9b0763 100644 --- a/emulator/frontends/pixels/src/bin/moa-genesis.rs +++ b/emulator/frontends/pixels/src/bin/moa-genesis.rs @@ -18,10 +18,18 @@ fn main() { mod web { use wasm_bindgen::prelude::*; use moa_genesis::utils; + use moa_pixels::LoadSystemFnHandle; + + use super::load_system; #[wasm_bindgen] pub fn smd_to_bin(input: Vec) -> Vec { utils::smd_to_bin(input).unwrap() } + + #[wasm_bindgen] + pub fn get_load_system_fn() -> LoadSystemFnHandle { + LoadSystemFnHandle::new(load_system) + } } diff --git a/emulator/frontends/pixels/src/frontend.rs b/emulator/frontends/pixels/src/frontend.rs index 52c9f51..7c5c72e 100644 --- a/emulator/frontends/pixels/src/frontend.rs +++ b/emulator/frontends/pixels/src/frontend.rs @@ -1,34 +1,41 @@ -use std::rc::Rc; - +use instant::Instant; use pixels::{Pixels, SurfaceTexture}; -use winit::dpi::LogicalSize; use winit::event::{Event, VirtualKeyCode, WindowEvent, ElementState}; use winit::event_loop::{ControlFlow, EventLoop}; -use winit::window::WindowBuilder; -use instant::Instant; -use moa_core::{System, Error, Clock}; +use moa_core::{System, Error}; use moa_core::host::{Host, WindowUpdater, ControllerDevice, ControllerEvent, ControllerUpdater, Audio, DummyAudio}; use moa_core::host::gfx::Frame; +use moa_common::audio::{AudioMixer, AudioSource, CpalAudioOutput}; use crate::settings; +use crate::create_window; -const WIDTH: u32 = 320; -const HEIGHT: u32 = 224; + +pub const WIDTH: u32 = 320; +pub const HEIGHT: u32 = 224; pub type LoadSystemFn = fn (&mut PixelsFrontend, Vec) -> Result; pub struct PixelsFrontend { updater: Option>, controller: Option>, + //mixer: Arc>, + //audio_output: CpalAudioOutput, } impl PixelsFrontend { pub fn new() -> PixelsFrontend { + settings::get().run = true; + //let mixer = AudioMixer::with_default_rate(); + //let audio_output = CpalAudioOutput::create_audio_output(mixer.lock().unwrap().get_sink()); + PixelsFrontend { controller: None, updater: None, + //mixer, + //audio_output, } } } @@ -49,102 +56,17 @@ impl Host for PixelsFrontend { } fn create_audio_source(&mut self) -> Result, Error> { + //let source = AudioSource::new(self.mixer.clone()); + //Ok(Box::new(source)) Ok(Box::new(DummyAudio())) } } -pub async fn run(load: LoadSystemFn) { - loop { - let host = PixelsFrontend::new(); - run_loop(host, load).await - } -} - -pub async fn run_loop(mut host: PixelsFrontend, load: LoadSystemFn) { +pub async fn run_loop(mut host: PixelsFrontend) { let event_loop = EventLoop::new(); - let window = { - let size = LogicalSize::new(WIDTH as f64, HEIGHT as f64); - WindowBuilder::new() - .with_title("Hello Pixels + Web") - .with_inner_size(size) - .with_min_inner_size(size) - .build(&event_loop) - .expect("WindowBuilder error") - }; - let window = Rc::new(window); + let window = create_window(&event_loop); - #[cfg(target_arch = "wasm32")] - { - use wasm_bindgen::JsCast; - use winit::platform::web::WindowExtWebSys; - - // Retrieve current width and height dimensions of browser client window - let get_window_size = || { - let client_window = web_sys::window().unwrap(); - LogicalSize::new( - client_window.inner_width().unwrap().as_f64().unwrap(), - client_window.inner_height().unwrap().as_f64().unwrap(), - ) - }; - - let window = Rc::clone(&window); - - // Initialize winit window with current dimensions of browser client - window.set_inner_size(get_window_size()); - - let client_window = web_sys::window().unwrap(); - - // Attach winit canvas to body element - web_sys::window() - .and_then(|win| win.document()) - .and_then(|doc| doc.body()) - .and_then(|body| { - body.append_child(&web_sys::Element::from(window.canvas())) - .ok() - }) - .expect("couldn't append canvas to document body"); - - // Listen for resize event on browser client. Adjust winit window dimensions - // on event trigger - let closure = wasm_bindgen::closure::Closure::wrap(Box::new(move |_e: web_sys::Event| { - let size = get_window_size(); - window.set_inner_size(size) - }) as Box); - client_window - .add_event_listener_with_callback("resize", closure.as_ref().unchecked_ref()) - .unwrap(); - closure.forget(); - - /* - let host = host.clone(); - let mut system = load(&mut host.lock().unwrap(), settings::get().rom_data.clone()).unwrap(); - let closure = wasm_bindgen::closure::Closure::wrap(Box::new(move |_e: web_sys::Event| { - let run_timer = Instant::now(); - let nanoseconds_per_frame = (16_600_000 as f32 * settings::get().speed) as Clock; - if let Err(err) = system.run_for(nanoseconds_per_frame) { - log::error!("{:?}", err); - } - log::info!("ran simulation for {:?}ms in {:?}ms", nanoseconds_per_frame / 1_000_000, run_timer.elapsed().as_millis()); - - let mut settings = settings::get(); - if settings.reset { - settings.reset = false; - - match load(&mut host.lock().unwrap(), settings.rom_data.clone()) { - Ok(s) => { system = s; }, - Err(err) => log::error!("{:?}", err), - } - } - }) as Box); - client_window - .set_interval_with_callback_and_timeout_and_arguments_0(closure.as_ref().unchecked_ref(), 17) - .unwrap(); - closure.forget(); - */ - } - - //let mut input = WinitInputHelper::new(); let mut pixels = { let window_size = window.inner_size(); let surface_texture = @@ -154,46 +76,43 @@ pub async fn run_loop(mut host: PixelsFrontend, load: LoadSystemFn) { .expect("Pixels error") }; + let mut last_size = (WIDTH, HEIGHT); let mut last_frame = Frame::new(WIDTH, HEIGHT); - let mut update_timer = Instant::now(); - let mut system = load(&mut host, settings::get().rom_data.clone()).unwrap(); + //let mut update_timer = Instant::now(); event_loop.run(move |event, _, control_flow| { // Draw the current frame if let Event::RedrawRequested(_) = event { settings::increment_frames(); - log::info!("updated after {:4}ms", update_timer.elapsed().as_millis()); - update_timer = Instant::now(); - let run_timer = Instant::now(); - let nanoseconds_per_frame = (16_600_000 as f32 * settings::get().speed) as Clock; - if let Err(err) = system.run_for(nanoseconds_per_frame) { - log::error!("{:?}", err); - } - log::info!("ran simulation for {:?}ms in {:?}ms", nanoseconds_per_frame / 1_000_000, run_timer.elapsed().as_millis()); + //log::warn!("updated after {:4}ms", update_timer.elapsed().as_millis()); + //update_timer = Instant::now(); if let Some(updater) = host.updater.as_mut() { - let buffer = pixels.get_frame(); if let Ok(frame) = updater.take_frame() { last_frame = frame; } - for y in 0..last_frame.height { - for x in 0..last_frame.width { - let pixel = last_frame.bitmap[((y * last_frame.width) + x) as usize]; - - let i = ((y * WIDTH) + x) as usize; - buffer[i * 4] = (pixel >> 16) as u8; - buffer[i * 4 + 1] = (pixel >> 8) as u8; - buffer[i * 4 + 2] = pixel as u8; - buffer[i * 4 + 3] = 255; - } + if (last_frame.width, last_frame.height) != last_size { + last_size = (last_frame.width, last_frame.height); + pixels.resize_buffer(last_frame.width, last_frame.height); } + + let buffer = pixels.get_frame(); + buffer + .chunks_mut(4) + .zip(last_frame.bitmap.iter()) + .for_each(|(dest, pixel)| { + dest[0] = (pixel >> 16) as u8; + dest[1] = (pixel >> 8) as u8; + dest[2] = *pixel as u8; + dest[3] = 255; + }); } if pixels .render() - .map_err(|e| println!("pixels.render() failed: {}", e)) + .map_err(|e| log::error!("pixels.render() failed: {}", e)) .is_err() { *control_flow = ControlFlow::Exit; @@ -203,34 +122,44 @@ pub async fn run_loop(mut host: PixelsFrontend, load: LoadSystemFn) { window.request_redraw(); } - let mut key = None; + // Process key inputs and pass them to the emulator's controller device if let Event::WindowEvent { event: WindowEvent::KeyboardInput { input, .. }, .. } = event { if let Some(keycode) = input.virtual_keycode { - match input.state { + let key = match input.state { ElementState::Pressed => { - key = map_controller_a(keycode, true); + map_controller_a(keycode, true) } ElementState::Released => { - key = map_controller_a(keycode, false); + map_controller_a(keycode, false) + } + }; + + if let Some(updater) = host.controller.as_mut() { + if let Some(key) = key { + updater.update_controller(key); } } } } - if let Some(updater) = host.controller.as_mut() { - if let Some(key) = key { - updater.update_controller(key); - } - } + // Check if the run flag is no longer true, and exit the loop + if !settings::get().run { + // Clear the screen + let buffer = pixels.get_frame(); + buffer.iter_mut().for_each(|byte| *byte = 0); - let mut settings = settings::get(); - if settings.reset { - settings.reset = false; - - match load(&mut host, settings.rom_data.clone()) { - Ok(s) => { system = s; }, - Err(err) => log::error!("{:?}", err), + if pixels + .render() + .map_err(|e| log::error!("pixels.render() failed: {}", e)) + .is_err() + { + *control_flow = ControlFlow::Exit; + return; } + + window.request_redraw(); + + *control_flow = ControlFlow::Exit; } }); } @@ -238,8 +167,8 @@ pub async fn run_loop(mut host: PixelsFrontend, load: LoadSystemFn) { pub fn map_controller_a(key: VirtualKeyCode, state: bool) -> Option { match key { VirtualKeyCode::A => { Some(ControllerEvent::ButtonA(state)) }, - VirtualKeyCode::O => { Some(ControllerEvent::ButtonB(state)) }, - VirtualKeyCode::E => { Some(ControllerEvent::ButtonC(state)) }, + VirtualKeyCode::S => { Some(ControllerEvent::ButtonB(state)) }, + VirtualKeyCode::D => { Some(ControllerEvent::ButtonC(state)) }, VirtualKeyCode::Up => { Some(ControllerEvent::DpadUp(state)) }, VirtualKeyCode::Down => { Some(ControllerEvent::DpadDown(state)) }, VirtualKeyCode::Left => { Some(ControllerEvent::DpadLeft(state)) }, diff --git a/emulator/frontends/pixels/src/lib.rs b/emulator/frontends/pixels/src/lib.rs index 95fc48c..7ac406f 100644 --- a/emulator/frontends/pixels/src/lib.rs +++ b/emulator/frontends/pixels/src/lib.rs @@ -6,10 +6,10 @@ pub use crate::frontend::{PixelsFrontend, LoadSystemFn}; #[cfg(target_arch = "wasm32")] pub mod web; #[cfg(target_arch = "wasm32")] -pub use crate::web::{start}; +pub use crate::web::{start, create_window, LoadSystemFnHandle}; #[cfg(not(target_arch = "wasm32"))] pub mod native; #[cfg(not(target_arch = "wasm32"))] -pub use crate::native::{start}; +pub use crate::native::{start, create_window}; diff --git a/emulator/frontends/pixels/src/native.rs b/emulator/frontends/pixels/src/native.rs index 3d8c0f0..dbf86d1 100644 --- a/emulator/frontends/pixels/src/native.rs +++ b/emulator/frontends/pixels/src/native.rs @@ -1,5 +1,10 @@ #![cfg(not(target_arch = "wasm32"))] +use std::rc::Rc; +use winit::dpi::LogicalSize; +use winit::event_loop::EventLoop; +use winit::window::{Window, WindowBuilder}; + use crate::frontend::{self, LoadSystemFn}; pub fn start(load: LoadSystemFn) { @@ -8,3 +13,16 @@ pub fn start(load: LoadSystemFn) { pollster::block_on(frontend::run(load)); } +pub fn create_window(event_loop: &EventLoop) -> Rc { + let size = LogicalSize::new(frontend::WIDTH as f64, frontend::HEIGHT as f64); + let window = WindowBuilder::new() + .with_title("Hello Pixels + Web") + .with_inner_size(size) + .with_min_inner_size(size) + .build(event_loop) + .expect("WindowBuilder error"); + + let window = Rc::new(window); + window +} + diff --git a/emulator/frontends/pixels/src/settings.rs b/emulator/frontends/pixels/src/settings.rs index c61485e..d77fa7a 100644 --- a/emulator/frontends/pixels/src/settings.rs +++ b/emulator/frontends/pixels/src/settings.rs @@ -6,7 +6,6 @@ static EMULATOR_OPTIONS: Mutex = Mutex::new(EmulatorSettings:: pub struct EmulatorSettings { pub rom_data: Vec, pub run: bool, - pub reset: bool, pub speed: f32, pub frames_since: usize, } @@ -15,8 +14,7 @@ impl EmulatorSettings { const fn new() -> Self { Self { rom_data: vec![], - run: true, - reset: false, + run: false, speed: 4.0, frames_since: 0, } @@ -42,8 +40,8 @@ pub fn increment_frames() { get().frames_since += 1; } -pub fn request_reset() { - get().reset = true; +pub fn request_stop() { + get().run = false; } pub fn toggle_run() { @@ -51,7 +49,3 @@ pub fn toggle_run() { options.run = !options.run; } -pub fn set_speed(speed: f32) { - get().speed = speed; -} - diff --git a/emulator/frontends/pixels/src/web.rs b/emulator/frontends/pixels/src/web.rs index f5f6c52..2efdfec 100644 --- a/emulator/frontends/pixels/src/web.rs +++ b/emulator/frontends/pixels/src/web.rs @@ -1,17 +1,24 @@ #![cfg(target_arch = "wasm32")] +use std::rc::Rc; +use instant::Instant; use wasm_bindgen::prelude::*; +use winit::dpi::LogicalSize; +use winit::event_loop::EventLoop; +use winit::window::{Window, WindowBuilder}; + +use moa_core::{Clock, System}; use crate::settings; -use crate::frontend::{self, LoadSystemFn}; +use crate::frontend::{self, PixelsFrontend, LoadSystemFn}; pub fn start(load: LoadSystemFn) { settings::set_rom_data(include_bytes!("../sonic.bin").to_vec()); std::panic::set_hook(Box::new(console_error_panic_hook::hook)); - console_log::init_with_level(log::Level::Info).expect("error initializing logger"); + console_log::init_with_level(log::Level::Warn).expect("error initializing logger"); - wasm_bindgen_futures::spawn_local(frontend::run(load)); + //wasm_bindgen_futures::spawn_local(frontend::run(load)); } #[wasm_bindgen] @@ -20,8 +27,8 @@ pub fn set_rom_data(rom_data: Vec) { } #[wasm_bindgen] -pub fn request_reset() { - settings::request_reset(); +pub fn request_stop() { + settings::request_stop(); } #[wasm_bindgen] @@ -29,9 +36,19 @@ pub fn toggle_run() { settings::toggle_run(); } +#[wasm_bindgen] +pub fn is_running() -> bool { + settings::get().run +} + #[wasm_bindgen] pub fn set_speed(speed: f32) { - settings::set_speed(speed); + settings::get().speed = speed; +} + +#[wasm_bindgen] +pub fn get_speed() -> f32 { + settings::get().speed } #[wasm_bindgen] @@ -39,3 +56,144 @@ pub fn get_frames_since() -> usize { settings::get_frames_since() } +#[wasm_bindgen] +pub struct HostHandle(PixelsFrontend); + +#[wasm_bindgen] +pub fn new_host() -> HostHandle { + HostHandle(PixelsFrontend::new()) +} + +#[wasm_bindgen] +pub fn host_run_loop(handle: HostHandle) { + wasm_bindgen_futures::spawn_local(frontend::run_loop(handle.0)); +} + +#[wasm_bindgen] +pub struct SystemHandle(System); + +#[wasm_bindgen] +pub struct LoadSystemFnHandle(LoadSystemFn); + +impl LoadSystemFnHandle { + pub fn new(load: LoadSystemFn) -> Self { + Self(load) + } +} + +#[wasm_bindgen] +pub fn load_system(handle: &mut HostHandle, load: LoadSystemFnHandle) -> SystemHandle { + let system = load.0(&mut handle.0, settings::get().rom_data.clone()).unwrap(); + SystemHandle(system) +} + +#[wasm_bindgen] +pub fn run_system_for(handle: &mut SystemHandle, nanos: u32) -> usize { + let run_timer = Instant::now(); + let nanoseconds_per_frame = nanos as Clock; + //let nanoseconds_per_frame = (16_600_000 as f32 * settings::get().speed) as Clock; + if let Err(err) = handle.0.run_for(nanoseconds_per_frame) { + log::error!("{:?}", err); + } + let run_time = run_timer.elapsed().as_millis(); + log::warn!("ran simulation for {:?}ms in {:?}ms", nanoseconds_per_frame / 1_000_000, run_time); + run_time as usize +} + +pub fn create_window(event_loop: &EventLoop) -> Rc { + use web_sys::HtmlCanvasElement; + use wasm_bindgen::JsCast; + use winit::platform::web::{WindowExtWebSys, WindowBuilderExtWebSys}; + + let canvas = web_sys::window() + .and_then(|win| win.document()) + .and_then(|doc| doc.get_element_by_id("video")) + .and_then(|el| el.dyn_into::().ok()) + .expect("document to have canvas"); + + let window = { + let size = LogicalSize::new(frontend::WIDTH as f64, frontend::HEIGHT as f64); + WindowBuilder::new() + .with_canvas(Some(canvas)) + .with_title("Hello Pixels + Web") + .with_inner_size(size) + .with_min_inner_size(size) + .build(event_loop) + .expect("WindowBuilder error") + }; + + let window = Rc::new(window); + + // Retrieve current width and height dimensions of browser client window + let get_window_size = || { + let client_window = web_sys::window().unwrap(); + LogicalSize::new( + client_window.inner_width().unwrap().as_f64().unwrap(), + client_window.inner_height().unwrap().as_f64().unwrap(), + ) + }; + + let window = Rc::clone(&window); + + // Initialize winit window with current dimensions of browser client + window.set_inner_size(get_window_size()); + + let client_window = web_sys::window().unwrap(); + + /* + // Attach winit canvas to body element + web_sys::window() + .and_then(|win| win.document()) + .and_then(|doc| doc.get_element_by_id("video-screen")) + .and_then(|el| { + while let Some(child) = el.first_child() { + el.remove_child(&child); + } + el.append_child(&web_sys::Element::from(window.canvas())) + .ok() + }) + .expect("couldn't append canvas to document body"); + */ + + { + let window = window.clone(); + // Listen for resize event on browser client. Adjust winit window dimensions + // on event trigger + let closure = wasm_bindgen::closure::Closure::wrap(Box::new(move |_e: web_sys::Event| { + let size = get_window_size(); + window.set_inner_size(size) + }) as Box); + client_window + .add_event_listener_with_callback("resize", closure.as_ref().unchecked_ref()) + .unwrap(); + closure.forget(); + } + + /* + let mut update_timer = Instant::now(); + let mut system = load(&mut host, settings::get().rom_data.clone()).unwrap(); + let closure = wasm_bindgen::closure::Closure::wrap(Box::new(move |_e: web_sys::Event| { + let run_timer = Instant::now(); + let nanoseconds_per_frame = (16_600_000 as f32 * settings::get().speed) as Clock; + if let Err(err) = system.run_for(nanoseconds_per_frame) { + log::error!("{:?}", err); + } + log::info!("ran simulation for {:?}ms in {:?}ms", nanoseconds_per_frame / 1_000_000, run_timer.elapsed().as_millis()); + + let settings = settings::get(); + if settings.run { + //match load(&mut host.lock().unwrap(), settings.rom_data.clone()) { + // Ok(s) => { system = s; }, + // Err(err) => log::error!("{:?}", err), + //} + } + }) as Box); + client_window + .set_interval_with_callback_and_timeout_and_arguments_0(closure.as_ref().unchecked_ref(), 17) + .unwrap(); + closure.forget(); + */ + + window +} +