use std::sync::Arc;
use std::time::Instant;
use crate::engine::startup_trace::{StartupCheckpoint, log_startup_progress};
use crate::engine::user_input::UserInput;
use crate::engine::{EngineError, EngineResult};
use winit::application::ApplicationHandler;
use winit::event::{ElementState, KeyEvent, WindowEvent};
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
use winit::keyboard::{Key, NamedKey};
use winit::window::{Window, WindowAttributes, WindowId};
pub struct Windowing;
impl Windowing {
pub fn run_app(universe: crate::engine::Universe) -> EngineResult<()> {
Self::run_app_with_input(universe, UserInput::new())
}
pub fn run_app_with_input(
universe: crate::engine::Universe,
user_input: UserInput,
) -> EngineResult<()> {
let event_loop = EventLoop::new()
.map_err(|error| EngineError::Windowing(format!("event loop init failed: {error}")))?;
event_loop.set_control_flow(ControlFlow::Poll);
let mut app = App {
window: None,
universe: Some(universe),
last_frame: None,
user_input,
startup_error: None,
};
event_loop.run_app(&mut app).map_err(|error| {
EngineError::Windowing(format!("window event loop failed: {error}"))
})?;
if let Some(error) = app.startup_error.take() {
return Err(error);
}
Ok(())
}
}
struct App {
window: Option<Arc<Window>>,
universe: Option<crate::engine::Universe>,
last_frame: Option<Instant>,
user_input: UserInput,
startup_error: Option<EngineError>,
}
impl ApplicationHandler for App {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
if self.window.is_some() {
return;
}
log_startup_progress(StartupCheckpoint::EventLoopResumed);
let preferred_window_size = self
.universe
.as_mut()
.and_then(|universe| universe.take_preferred_window_size())
.unwrap_or([1024, 768]);
let attrs: WindowAttributes = Window::default_attributes()
.with_title("mittens 0.7.0")
.with_inner_size(winit::dpi::LogicalSize::new(
preferred_window_size[0] as f64,
preferred_window_size[1] as f64,
))
.with_resizable(true);
let window = match event_loop.create_window(attrs) {
Ok(window) => window,
Err(error) => {
self.startup_error = Some(EngineError::Windowing(format!(
"window creation failed: {error}"
)));
event_loop.exit();
return;
}
};
let window = Arc::new(window);
log_startup_progress(StartupCheckpoint::WindowCreated);
if let Some(universe) = self.universe.as_mut() {
if let Err(error) = universe.init_renderer_for_window(&window) {
self.startup_error = Some(EngineError::Windowing(format!(
"renderer init failed: {error}"
)));
event_loop.exit();
return;
}
}
log_startup_progress(StartupCheckpoint::RendererInitialized);
self.window = Some(window);
self.last_frame = Some(Instant::now());
if let Some(w) = &self.window {
w.request_redraw();
}
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
let _was_input_event = self.user_input.handle_window_event(&event);
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::KeyboardInput {
event:
KeyEvent {
logical_key: Key::Named(NamedKey::Escape),
state: ElementState::Pressed,
..
},
..
} => event_loop.exit(),
WindowEvent::Resized(size) => {
if let Some(universe) = self.universe.as_mut() {
universe.resize_renderer(size);
}
if let Some(w) = &self.window {
w.request_redraw();
}
}
WindowEvent::RedrawRequested => {
log_startup_progress(StartupCheckpoint::FirstRedrawRequested);
self.user_input.start_frame();
let now = Instant::now();
let dt = self
.last_frame
.replace(now)
.map(|prev| (now - prev).as_secs_f32())
.unwrap_or(0.0);
let universe = self.universe.as_mut().expect("universe missing");
universe.update(dt, self.user_input.state());
universe.render();
self.user_input.end_frame();
if universe.exit_requested() {
event_loop.exit();
return;
}
if let Some(w) = &self.window {
w.request_redraw();
}
}
_ => {}
}
}
}