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 window_builder = WindowBuilder::new()
63 .with_title(config.title)
64 .with_inner_size(PhysicalSize::new(config.width, config.height));
65
66 #[cfg(target_arch = "wasm32")]
67 let window = {
68 use wasm_bindgen::JsCast;
69 use winit::platform::web::WindowBuilderExtWebSys;
70
71 let web_window = web_sys::window().expect("no global `window` exists");
72 let document = web_window
73 .document()
74 .expect("should have a document on window");
75 let canvas = document
76 .get_element_by_id("canvas")
77 .expect("no element with id `canvas` found — add <canvas id=\"wgpu_canvas\"></canvas> to index.html")
78 .unchecked_into::<web_sys::HtmlCanvasElement>();
79
80 Arc::new(
81 window_builder
82 .with_canvas(Some(canvas))
83 .build(&event_loop)
84 .unwrap(),
85 )
86 };
87
88 #[cfg(not(target_arch = "wasm32"))]
89 let window = Arc::new(window_builder.build(&event_loop).unwrap());
90
91 Self {
92 window,
93 event_loop,
94 input: Input::new(),
95 }
96 }
97
98 fn size(handle: &Self::Handle) -> (u32, u32) {
99 let s = handle.inner_size();
100 (s.width, s.height)
101 }
102
103 fn exposed(&self) -> Self::Exposed {
104 self.input.clone()
105 }
106
107 fn handle(&self) -> &Self::Handle {
108 &self.window
109 }
110}
111
112impl WindowRunner for WinitWindow {
113 fn run(self, mut on_frame: impl FnMut() + 'static) {
114 let Self {
115 window,
116 event_loop,
117 input,
118 } = self;
119
120 event_loop
121 .run(move |event, elwt| {
122 let stepped = input.update(&event);
123
124 match &event {
125 Event::WindowEvent {
126 event: WindowEvent::CloseRequested,
127 ..
128 } => elwt.exit(),
129 _ => {}
130 }
131
132 if stepped {
133 on_frame();
134 window.request_redraw();
135 }
136 })
137 .unwrap();
138 }
139}
140
141impl PresentableWindow for WinitWindow {}