1use std::sync::Arc;
9
10pub enum EventPhase {
12 Run(usize),
14 Quit,
16 Wait,
18}
19
20pub trait FrendererEvents<T> {
22 fn handle_event(
27 &mut self,
28 clock: &mut crate::clock::Clock,
29 window: &Arc<winit::window::Window>,
30 evt: &winit::event::Event<T>,
31 target: &winit::event_loop::EventLoopWindowTarget<T>,
32 input: &mut crate::input::Input,
33 ) -> EventPhase;
34}
35impl<T> FrendererEvents<T> for crate::Renderer {
36 fn handle_event(
37 &mut self,
38 clock: &mut crate::clock::Clock,
39 window: &Arc<winit::window::Window>,
40 evt: &winit::event::Event<T>,
41 _target: &winit::event_loop::EventLoopWindowTarget<T>,
42 input: &mut crate::input::Input,
43 ) -> EventPhase {
44 use winit::event::{Event, WindowEvent};
45 match evt {
46 Event::Resumed if self.surface().is_none() => {
47 self.create_surface(Arc::clone(window));
48 EventPhase::Wait
49 }
50 Event::WindowEvent {
51 event: WindowEvent::CloseRequested,
52 ..
53 } => EventPhase::Quit,
54 winit::event::Event::WindowEvent {
55 event: winit::event::WindowEvent::Resized(size),
56 ..
57 } => {
58 if !self.gpu.is_web() {
60 self.resize_surface(size.width, size.height);
61 }
62 window.request_redraw();
63 EventPhase::Wait
64 }
65 Event::WindowEvent {
66 event: WindowEvent::RedrawRequested,
67 ..
68 } => {
69 let steps = clock.tick();
70 window.request_redraw();
71 EventPhase::Run(steps)
72 }
73 event => {
74 input.process_input_event(event);
75 EventPhase::Wait
76 }
77 }
78 }
79}
80impl<T> FrendererEvents<T> for crate::Immediate {
81 fn handle_event(
82 &mut self,
83 clock: &mut crate::clock::Clock,
84 window: &Arc<winit::window::Window>,
85 evt: &winit::event::Event<T>,
86 target: &winit::event_loop::EventLoopWindowTarget<T>,
87 input: &mut crate::input::Input,
88 ) -> EventPhase {
89 self.renderer
90 .handle_event(clock, window, evt, target, input)
91 }
92}
93pub struct Driver {
95 builder: winit::window::WindowBuilder,
96 render_size: Option<(u32, u32)>,
97}
98#[cfg(all(target_arch = "wasm32", feature = "winit"))]
99pub mod web_error {
100 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
101 pub enum RuntimeError {
102 NoCanvas,
103 NoDocument,
104 NoBody,
105 }
106 impl std::fmt::Display for RuntimeError {
107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108 <Self as std::fmt::Debug>::fmt(self, f)
109 }
110 }
111 impl std::error::Error for RuntimeError {}
112}
113
114struct NoopWaker();
115impl std::task::Wake for NoopWaker {
116 fn wake(self: std::sync::Arc<Self>) {
117 }
119}
120
121impl Driver {
122 pub fn new(builder: winit::window::WindowBuilder, render_size: Option<(u32, u32)>) -> Self {
124 Self {
125 builder,
126 render_size,
127 }
128 }
129 pub fn run_event_loop<T: 'static, U: 'static>(
157 self,
158 init_cb: impl FnOnce(std::sync::Arc<winit::window::Window>, crate::Renderer) -> U + 'static,
159 mut handler: impl FnMut(winit::event::Event<T>, &winit::event_loop::EventLoopWindowTarget<T>, &mut U)
160 + 'static,
161 ) -> Result<(), Box<dyn std::error::Error>> {
162 enum DriverState<U: 'static> {
163 WaitingForResume(winit::window::WindowBuilder),
164 PollingFuture(
165 Arc<winit::window::Window>,
166 #[allow(clippy::type_complexity)]
167 std::pin::Pin<
168 Box<
169 dyn std::future::Future<
170 Output = Result<crate::Renderer, Box<dyn std::error::Error>>,
171 >,
172 >,
173 >,
174 ),
175 Running(U),
176 InsideLoop,
178 }
179 use winit::event_loop::EventLoop;
180 let Self {
181 builder,
182 render_size,
183 } = self;
184 prepare_logging()?;
185 let event_loop: EventLoop<T> =
186 winit::event_loop::EventLoopBuilder::with_user_event().build()?;
187 let instance = Arc::new(wgpu::Instance::default());
188 let waker = Arc::new(NoopWaker()).into();
189 let mut init_cb = Some(init_cb);
190 let driver_state = std::cell::Cell::new(DriverState::WaitingForResume(builder));
191 let cb = move |event, target: &winit::event_loop::EventLoopWindowTarget<_>| {
192 target.set_control_flow(winit::event_loop::ControlFlow::Wait);
193 driver_state.set(match driver_state.replace(DriverState::InsideLoop) {
194 DriverState::WaitingForResume(builder) => {
195 if let winit::event::Event::Resumed = event {
196 let window = Arc::new(builder.build(target).unwrap());
197 prepare_window(&window);
198 let surface = instance.create_surface(Arc::clone(&window)).unwrap();
199 let wsz = window.inner_size();
200 let sz = render_size.unwrap_or((wsz.width, wsz.height));
201 let future = Box::pin(crate::Renderer::with_surface(
202 sz.0,
203 sz.1,
204 wsz.width,
205 wsz.height,
206 Arc::clone(&instance),
207 Some(surface),
208 ));
209 DriverState::PollingFuture(window, future)
210 } else {
211 DriverState::WaitingForResume(builder)
212 }
213 }
214 DriverState::PollingFuture(window, mut future) => {
215 let mut cx = std::task::Context::from_waker(&waker);
216 if let std::task::Poll::Ready(frend) = future.as_mut().poll(&mut cx) {
217 let frenderer = frend.unwrap();
218 let userdata = init_cb.take().unwrap()(Arc::clone(&window), frenderer);
219 DriverState::Running(userdata)
220 } else {
221 target.set_control_flow(winit::event_loop::ControlFlow::Poll);
223 DriverState::PollingFuture(window, future)
224 }
225 }
226 DriverState::Running(mut userdata) => {
227 handler(event, target, &mut userdata);
228 DriverState::Running(userdata)
229 }
230 DriverState::InsideLoop => {
231 panic!("driver state loop unexpectedly reentrant");
232 }
233 });
234 };
235 #[cfg(not(target_arch = "wasm32"))]
236 {
237 Ok(event_loop.run(cb)?)
238 }
239 #[cfg(target_arch = "wasm32")]
240 {
241 use winit::platform::web::EventLoopExtWebSys;
242 Ok(event_loop.spawn(cb))
243 }
244 }
245}
246
247#[allow(unused_variables)]
252pub fn prepare_window(window: &winit::window::Window) {
253 #[cfg(target_arch = "wasm32")]
254 {
255 use self::web_error::RuntimeError;
256 use crate::wgpu::web_sys;
257 use winit::platform::web::WindowExtWebSys;
258 let doc = web_sys::window()
259 .ok_or(RuntimeError::NoBody)
260 .unwrap()
261 .document()
262 .unwrap();
263 let canvas = window.canvas().ok_or(RuntimeError::NoCanvas).unwrap();
264 doc.body()
265 .ok_or(RuntimeError::NoBody)
266 .unwrap()
267 .append_child(&canvas)
268 .unwrap();
269 }
270}
271
272pub fn prepare_logging() -> Result<(), Box<dyn std::error::Error>> {
276 #[cfg(not(target_arch = "wasm32"))]
277 {
278 env_logger::init();
279 Ok(())
280 }
281 #[cfg(target_arch = "wasm32")]
282 {
283 std::panic::set_hook(Box::new(console_error_panic_hook::hook));
284 console_log::init_with_level(log::Level::Warn)?;
285 Ok(())
286 }
287}