1use std::ops::Deref;
2use std::sync::{Arc, Mutex, MutexGuard};
3
4#[cfg(not(target_arch = "wasm32"))]
5use winit::dpi::PhysicalSize;
6use winit::{
7 event::{Event, WindowEvent},
8 event_loop::EventLoop,
9 window::{Window, WindowBuilder},
10};
11use winit_input_helper::WinitInputHelper;
12
13use crate::rendering::window::{PresentableWindow, WindowConfig, WindowProvider, WindowRunner};
14
15#[derive(Clone)]
21pub struct Input(Arc<Mutex<WinitInputHelper>>);
22
23impl Input {
24 fn new() -> Self {
25 Self(Arc::new(Mutex::new(WinitInputHelper::new())))
26 }
27
28 pub fn get(&self) -> InputGuard<'_> {
32 InputGuard(self.0.lock().unwrap())
33 }
34
35 fn update(&self, event: &Event<()>) -> bool {
36 return self.0.lock().unwrap().update(event);
37 }
38}
39
40pub struct InputGuard<'a>(MutexGuard<'a, WinitInputHelper>);
41
42impl Deref for InputGuard<'_> {
43 type Target = WinitInputHelper;
44 fn deref(&self) -> &Self::Target {
45 &self.0
46 }
47}
48
49pub struct WinitWindow {
50 window: Arc<Window>,
51 event_loop: EventLoop<()>,
52 input: Input,
53}
54
55impl WindowProvider for WinitWindow {
56 type Handle = Arc<Window>;
57 type Exposed = Input;
58
59 fn create(config: &WindowConfig) -> Self {
60 let event_loop = EventLoop::new().unwrap();
61 event_loop.set_control_flow(winit::event_loop::ControlFlow::Poll);
62
63 #[cfg_attr(target_arch = "wasm32", allow(unused_mut))]
64 let mut window_builder = WindowBuilder::new().with_title(config.title.clone());
65
66 #[cfg(not(target_arch = "wasm32"))]
67 {
68 window_builder =
69 window_builder.with_inner_size(PhysicalSize::new(config.width, config.height));
70 }
71
72 #[cfg(target_arch = "wasm32")]
73 let window = {
74 use wasm_bindgen::JsCast;
75 use winit::platform::web::WindowBuilderExtWebSys;
76
77 console_error_panic_hook::set_once();
85
86 let web_window = web_sys::window().expect("no global `window` exists");
87 let document = web_window
88 .document()
89 .expect("should have a document on window");
90 let canvas = document
91 .get_element_by_id("wgpu_canvas")
92 .expect("no element with id `wgpu_canvas` found — add <canvas id=\"wgpu_canvas\"></canvas> to index.html")
93 .unchecked_into::<web_sys::HtmlCanvasElement>();
94
95 let window = Arc::new(
96 window_builder
97 .with_canvas(Some(canvas))
98 .build(&event_loop)
99 .unwrap(),
100 );
101
102 let sync_size = {
107 let window = window.clone();
108 move || {
109 let web_window = web_sys::window().expect("no global `window` exists");
110 let width = web_window.inner_width().unwrap().as_f64().unwrap();
111 let height = web_window.inner_height().unwrap().as_f64().unwrap();
112 let _ = window.request_inner_size(winit::dpi::LogicalSize::new(width, height));
113 }
114 };
115 sync_size();
116
117 let closure =
118 wasm_bindgen::closure::Closure::<dyn FnMut()>::new(sync_size).into_js_value();
119 web_window
120 .add_event_listener_with_callback("resize", closure.unchecked_ref())
121 .expect("failed to add `resize` listener");
122
123 window
124 };
125
126 #[cfg(not(target_arch = "wasm32"))]
127 let window = Arc::new(window_builder.build(&event_loop).unwrap());
128
129 Self {
130 window,
131 event_loop,
132 input: Input::new(),
133 }
134 }
135
136 fn size(handle: &Self::Handle) -> (u32, u32) {
137 let s = handle.inner_size();
138 (s.width, s.height)
139 }
140
141 fn exposed(&self) -> Self::Exposed {
142 self.input.clone()
143 }
144
145 fn handle(&self) -> &Self::Handle {
146 &self.window
147 }
148}
149
150impl WindowRunner for WinitWindow {
151 fn run(self, mut on_frame: impl FnMut() + 'static) {
152 let Self {
153 window,
154 event_loop,
155 input,
156 } = self;
157
158 #[cfg(target_arch = "wasm32")]
166 window.request_redraw();
167
168 event_loop
169 .run(move |event, elwt| {
170 let stepped = input.update(&event);
171
172 match &event {
173 Event::WindowEvent {
174 event: WindowEvent::CloseRequested,
175 ..
176 } => elwt.exit(),
177 #[cfg(target_arch = "wasm32")]
178 Event::WindowEvent {
179 event: WindowEvent::RedrawRequested,
180 ..
181 } => {
182 on_frame();
183 window.request_redraw();
184 }
185 _ => {}
186 }
187
188 #[cfg(not(target_arch = "wasm32"))]
189 if stepped {
190 on_frame();
191 window.request_redraw();
192 }
193 #[cfg(target_arch = "wasm32")]
194 let _ = stepped;
195 })
196 .unwrap();
197 }
198}
199
200impl PresentableWindow for WinitWindow {}