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