mittens_engine/engine/
windowing.rs1use std::sync::Arc;
2use std::time::Instant;
3
4use crate::engine::startup_trace::{StartupCheckpoint, log_startup_progress};
5use crate::engine::user_input::UserInput;
6use crate::engine::{EngineError, EngineResult};
7
8use winit::application::ApplicationHandler;
9use winit::event::{ElementState, KeyEvent, WindowEvent};
10use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
11use winit::keyboard::{Key, NamedKey};
12use winit::window::{Window, WindowAttributes, WindowId};
13
14pub struct Windowing;
16
17impl Windowing {
18 pub fn run_app(universe: crate::engine::Universe) -> EngineResult<()> {
20 Self::run_app_with_input(universe, UserInput::new())
21 }
22
23 pub fn run_app_with_input(
25 universe: crate::engine::Universe,
26 user_input: UserInput,
27 ) -> EngineResult<()> {
28 let event_loop = EventLoop::new()
29 .map_err(|error| EngineError::Windowing(format!("event loop init failed: {error}")))?;
30 event_loop.set_control_flow(ControlFlow::Poll);
31
32 let mut app = App {
33 window: None,
34 universe: Some(universe),
35 last_frame: None,
36 user_input,
37 startup_error: None,
38 };
39
40 event_loop.run_app(&mut app).map_err(|error| {
41 EngineError::Windowing(format!("window event loop failed: {error}"))
42 })?;
43
44 if let Some(error) = app.startup_error.take() {
45 return Err(error);
46 }
47
48 Ok(())
49 }
50}
51
52struct App {
53 window: Option<Arc<Window>>,
54 universe: Option<crate::engine::Universe>,
55 last_frame: Option<Instant>,
56 user_input: UserInput,
57 startup_error: Option<EngineError>,
58}
59
60impl ApplicationHandler for App {
61 fn resumed(&mut self, event_loop: &ActiveEventLoop) {
62 if self.window.is_some() {
63 return;
64 }
65 log_startup_progress(StartupCheckpoint::EventLoopResumed);
66
67 let preferred_window_size = self
68 .universe
69 .as_mut()
70 .and_then(|universe| universe.take_preferred_window_size())
71 .unwrap_or([1024, 768]);
72
73 let attrs: WindowAttributes = Window::default_attributes()
74 .with_title("mittens 0.5.1")
75 .with_inner_size(winit::dpi::LogicalSize::new(
76 preferred_window_size[0] as f64,
77 preferred_window_size[1] as f64,
78 ))
79 .with_resizable(true);
80
81 let window = match event_loop.create_window(attrs) {
82 Ok(window) => window,
83 Err(error) => {
84 self.startup_error = Some(EngineError::Windowing(format!(
85 "window creation failed: {error}"
86 )));
87 event_loop.exit();
88 return;
89 }
90 };
91 let window = Arc::new(window);
92 log_startup_progress(StartupCheckpoint::WindowCreated);
93
94 if let Some(universe) = self.universe.as_mut() {
96 if let Err(error) = universe.init_renderer_for_window(&window) {
97 self.startup_error = Some(EngineError::Windowing(format!(
98 "renderer init failed: {error}"
99 )));
100 event_loop.exit();
101 return;
102 }
103 }
104 log_startup_progress(StartupCheckpoint::RendererInitialized);
105
106 self.window = Some(window);
107 self.last_frame = Some(Instant::now());
108
109 if let Some(w) = &self.window {
110 w.request_redraw();
111 }
112 }
113
114 fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
115 let _was_input_event = self.user_input.handle_window_event(&event);
118
119 match event {
120 WindowEvent::CloseRequested => event_loop.exit(),
121
122 WindowEvent::KeyboardInput {
123 event:
124 KeyEvent {
125 logical_key: Key::Named(NamedKey::Escape),
126 state: ElementState::Pressed,
127 ..
128 },
129 ..
130 } => event_loop.exit(),
131
132 WindowEvent::Resized(size) => {
133 if let Some(universe) = self.universe.as_mut() {
134 universe.resize_renderer(size);
135 }
136 if let Some(w) = &self.window {
137 w.request_redraw();
139 }
140 }
141
142 WindowEvent::RedrawRequested => {
143 log_startup_progress(StartupCheckpoint::FirstRedrawRequested);
144 self.user_input.start_frame();
147
148 let now = Instant::now();
149 let dt = self
150 .last_frame
151 .replace(now)
152 .map(|prev| (now - prev).as_secs_f32())
153 .unwrap_or(0.0);
154
155 let universe = self.universe.as_mut().expect("universe missing");
156
157 universe.update(dt, self.user_input.state());
158
159 universe.render();
160
161 self.user_input.end_frame();
163
164 if let Some(w) = &self.window {
165 w.request_redraw();
167 }
168 }
169
170 _ => {}
171 }
172 }
173}