1use winit::application::ApplicationHandler;
6use winit::dpi::PhysicalSize;
7use winit::event::{ElementState, WindowEvent};
8use winit::event_loop::ActiveEventLoop;
9use winit::window::WindowId;
10
11use crate::input::{InputEvent, KeyCode, MouseButton};
12use crate::Result;
13
14pub struct EventLoop {
16 inner: winit::event_loop::EventLoop<()>,
17}
18
19impl EventLoop {
20 pub fn new() -> Result<Self> {
26 let inner = winit::event_loop::EventLoop::new()?;
27 Ok(Self { inner })
28 }
29
30 #[must_use]
32 pub fn inner(&self) -> &winit::event_loop::EventLoop<()> {
33 &self.inner
34 }
35
36 pub fn run<F>(self, mut callback: F) -> Result<()>
44 where
45 F: FnMut(AppEvent, &mut ControlFlow) + 'static,
46 {
47 let mut control_flow = ControlFlow::default();
48 let mut handler = CallbackHandler {
49 callback: &mut callback,
50 control_flow: &mut control_flow,
51 };
52
53 self.inner.run_app(&mut handler)?;
54 Ok(())
55 }
56}
57
58#[derive(Debug, Clone)]
60pub enum AppEvent {
61 Resized {
63 width: u32,
65 height: u32,
67 },
68 CloseRequested,
70 RedrawRequested,
72 Focused(bool),
74 Input(InputEvent),
76 LoopExiting,
78 NewEvents,
80 AboutToWait,
82}
83
84#[derive(Debug, Default)]
86pub struct ControlFlow {
87 should_exit: bool,
88}
89
90impl ControlFlow {
91 pub fn exit(&mut self) {
93 self.should_exit = true;
94 }
95
96 #[must_use]
98 pub fn should_exit(&self) -> bool {
99 self.should_exit
100 }
101}
102
103struct CallbackHandler<'a, F>
105where
106 F: FnMut(AppEvent, &mut ControlFlow),
107{
108 callback: &'a mut F,
109 control_flow: &'a mut ControlFlow,
110}
111
112impl<F> ApplicationHandler for CallbackHandler<'_, F>
113where
114 F: FnMut(AppEvent, &mut ControlFlow),
115{
116 fn resumed(&mut self, _event_loop: &ActiveEventLoop) {
117 }
119
120 fn window_event(
121 &mut self,
122 event_loop: &ActiveEventLoop,
123 _window_id: WindowId,
124 event: WindowEvent,
125 ) {
126 let app_event = match event {
127 WindowEvent::CloseRequested => Some(AppEvent::CloseRequested),
128 WindowEvent::Resized(PhysicalSize { width, height }) => {
129 Some(AppEvent::Resized { width, height })
130 }
131 WindowEvent::RedrawRequested => Some(AppEvent::RedrawRequested),
132 WindowEvent::Focused(focused) => Some(AppEvent::Focused(focused)),
133 WindowEvent::KeyboardInput { event, .. } => {
134 if let Some(key_code) = convert_key_code(event.physical_key) {
135 let input_event = match event.state {
136 ElementState::Pressed => InputEvent::KeyPressed(key_code),
137 ElementState::Released => InputEvent::KeyReleased(key_code),
138 };
139 Some(AppEvent::Input(input_event))
140 } else {
141 None
142 }
143 }
144 WindowEvent::MouseInput { state, button, .. } => {
145 let mouse_button = convert_mouse_button(button);
146 let input_event = match state {
147 ElementState::Pressed => InputEvent::MousePressed(mouse_button),
148 ElementState::Released => InputEvent::MouseReleased(mouse_button),
149 };
150 Some(AppEvent::Input(input_event))
151 }
152 WindowEvent::CursorMoved { position, .. } => {
153 Some(AppEvent::Input(InputEvent::MouseMoved {
154 x: position.x,
155 y: position.y,
156 }))
157 }
158 WindowEvent::MouseWheel { delta, .. } => {
159 let (x, y) = match delta {
160 winit::event::MouseScrollDelta::LineDelta(x, y) => (f64::from(x), f64::from(y)),
161 winit::event::MouseScrollDelta::PixelDelta(pos) => (pos.x, pos.y),
162 };
163 Some(AppEvent::Input(InputEvent::MouseWheel {
164 delta_x: x,
165 delta_y: y,
166 }))
167 }
168 _ => None,
169 };
170
171 if let Some(event) = app_event {
172 (self.callback)(event, self.control_flow);
173 }
174
175 if self.control_flow.should_exit() {
176 event_loop.exit();
177 }
178 }
179
180 fn new_events(&mut self, _event_loop: &ActiveEventLoop, _cause: winit::event::StartCause) {
181 (self.callback)(AppEvent::NewEvents, self.control_flow);
182 }
183
184 fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
185 (self.callback)(AppEvent::AboutToWait, self.control_flow);
186 if self.control_flow.should_exit() {
187 event_loop.exit();
188 }
189 }
190
191 fn exiting(&mut self, _event_loop: &ActiveEventLoop) {
192 (self.callback)(AppEvent::LoopExiting, self.control_flow);
193 }
194}
195
196fn convert_key_code(key: winit::keyboard::PhysicalKey) -> Option<KeyCode> {
198 use winit::keyboard::{KeyCode as WinitKey, PhysicalKey};
199
200 match key {
201 PhysicalKey::Code(code) => Some(match code {
202 WinitKey::KeyA => KeyCode::A,
203 WinitKey::KeyB => KeyCode::B,
204 WinitKey::KeyC => KeyCode::C,
205 WinitKey::KeyD => KeyCode::D,
206 WinitKey::KeyE => KeyCode::E,
207 WinitKey::KeyF => KeyCode::F,
208 WinitKey::KeyG => KeyCode::G,
209 WinitKey::KeyH => KeyCode::H,
210 WinitKey::KeyI => KeyCode::I,
211 WinitKey::KeyJ => KeyCode::J,
212 WinitKey::KeyK => KeyCode::K,
213 WinitKey::KeyL => KeyCode::L,
214 WinitKey::KeyM => KeyCode::M,
215 WinitKey::KeyN => KeyCode::N,
216 WinitKey::KeyO => KeyCode::O,
217 WinitKey::KeyP => KeyCode::P,
218 WinitKey::KeyQ => KeyCode::Q,
219 WinitKey::KeyR => KeyCode::R,
220 WinitKey::KeyS => KeyCode::S,
221 WinitKey::KeyT => KeyCode::T,
222 WinitKey::KeyU => KeyCode::U,
223 WinitKey::KeyV => KeyCode::V,
224 WinitKey::KeyW => KeyCode::W,
225 WinitKey::KeyX => KeyCode::X,
226 WinitKey::KeyY => KeyCode::Y,
227 WinitKey::KeyZ => KeyCode::Z,
228 WinitKey::Digit0 => KeyCode::Num0,
229 WinitKey::Digit1 => KeyCode::Num1,
230 WinitKey::Digit2 => KeyCode::Num2,
231 WinitKey::Digit3 => KeyCode::Num3,
232 WinitKey::Digit4 => KeyCode::Num4,
233 WinitKey::Digit5 => KeyCode::Num5,
234 WinitKey::Digit6 => KeyCode::Num6,
235 WinitKey::Digit7 => KeyCode::Num7,
236 WinitKey::Digit8 => KeyCode::Num8,
237 WinitKey::Digit9 => KeyCode::Num9,
238 WinitKey::Escape => KeyCode::Escape,
239 WinitKey::Enter => KeyCode::Enter,
240 WinitKey::Space => KeyCode::Space,
241 WinitKey::Tab => KeyCode::Tab,
242 WinitKey::Backspace => KeyCode::Backspace,
243 WinitKey::ShiftLeft | WinitKey::ShiftRight => KeyCode::Shift,
244 WinitKey::ControlLeft | WinitKey::ControlRight => KeyCode::Control,
245 WinitKey::AltLeft | WinitKey::AltRight => KeyCode::Alt,
246 WinitKey::ArrowUp => KeyCode::Up,
247 WinitKey::ArrowDown => KeyCode::Down,
248 WinitKey::ArrowLeft => KeyCode::Left,
249 WinitKey::ArrowRight => KeyCode::Right,
250 WinitKey::F1 => KeyCode::F1,
251 WinitKey::F2 => KeyCode::F2,
252 WinitKey::F3 => KeyCode::F3,
253 WinitKey::F4 => KeyCode::F4,
254 WinitKey::F5 => KeyCode::F5,
255 WinitKey::F6 => KeyCode::F6,
256 WinitKey::F7 => KeyCode::F7,
257 WinitKey::F8 => KeyCode::F8,
258 WinitKey::F9 => KeyCode::F9,
259 WinitKey::F10 => KeyCode::F10,
260 WinitKey::F11 => KeyCode::F11,
261 WinitKey::F12 => KeyCode::F12,
262 _ => return None,
263 }),
264 PhysicalKey::Unidentified(_) => None,
265 }
266}
267
268fn convert_mouse_button(button: winit::event::MouseButton) -> MouseButton {
270 match button {
271 winit::event::MouseButton::Left => MouseButton::Left,
272 winit::event::MouseButton::Right => MouseButton::Right,
273 winit::event::MouseButton::Middle => MouseButton::Middle,
274 winit::event::MouseButton::Back => MouseButton::Back,
275 winit::event::MouseButton::Forward => MouseButton::Forward,
276 winit::event::MouseButton::Other(id) => MouseButton::Other(id),
277 }
278}
279