use crate::{Core, ShaderManager};
use log::error;
use winit::{
application::ApplicationHandler,
dpi::LogicalSize,
event::*,
event_loop::{ActiveEventLoop, EventLoop},
window::WindowAttributes,
};
pub struct ShaderApp {
window_title: String,
window_size: (u32, u32),
core: Option<Core>,
}
impl ShaderApp {
pub fn new(window_title: &str, width: u32, height: u32) -> (Self, EventLoop<()>) {
let event_loop = EventLoop::builder()
.build()
.expect("Failed to create event loop");
let app = Self {
window_title: String::from(window_title),
window_size: (width, height),
core: None,
};
(app, event_loop)
}
pub fn run<S: ShaderManager + 'static>(
self,
event_loop: EventLoop<()>,
shader_creator: impl FnOnce(&Core) -> S + 'static,
) -> Result<(), Box<dyn std::error::Error>> {
let mut handler = ShaderAppHandler {
app: self,
shader_creator: Some(Box::new(shader_creator)),
shader: None,
first_render: true,
};
Ok(event_loop.run_app(&mut handler)?)
}
pub fn core(&self) -> Option<&Core> {
self.core.as_ref()
}
}
struct ShaderAppHandler<S: ShaderManager> {
app: ShaderApp,
shader_creator: Option<Box<dyn FnOnce(&Core) -> S + 'static>>,
shader: Option<S>,
first_render: bool,
}
impl<S: ShaderManager> ApplicationHandler for ShaderAppHandler<S> {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let window_attributes = WindowAttributes::default()
.with_inner_size(LogicalSize::new(
self.app.window_size.0,
self.app.window_size.1,
))
.with_title(&self.app.window_title)
.with_resizable(true);
let window = event_loop
.create_window(window_attributes)
.expect("Failed to create window");
window.set_window_level(winit::window::WindowLevel::AlwaysOnTop);
let core = pollster::block_on(Core::new(window));
if let Some(shader_creator) = self.shader_creator.take() {
let shader = shader_creator(&core);
self.shader = Some(shader);
}
self.app.core = Some(core);
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
window_id: winit::window::WindowId,
event: WindowEvent,
) {
if let (Some(core), Some(shader)) = (&self.app.core, &mut self.shader) {
if window_id == core.window().id() && !shader.handle_input(core, &event) {
match event {
WindowEvent::CloseRequested => {
event_loop.exit();
}
WindowEvent::Resized(size) => {
if let Some(core) = &mut self.app.core {
if core.size == size {
return;
}
core.resize(size);
shader.resize(core);
}
}
WindowEvent::RedrawRequested => {
shader.update(core);
match shader.render(core) {
Ok(_) => {
if self.first_render {
self.first_render = false;
}
}
Err(crate::SurfaceError::SkipFrame) => {}
Err(crate::SurfaceError::Lost | crate::SurfaceError::Outdated) => {
if let Some(core) = &mut self.app.core {
core.resize(core.size);
}
}
Err(crate::SurfaceError::OutOfMemory) => {
error!("GPU out of memory, exiting");
event_loop.exit();
}
}
}
_ => {}
}
}
}
}
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
if let Some(core) = &self.app.core {
core.window().request_redraw();
}
}
fn new_events(&mut self, _event_loop: &ActiveEventLoop, _cause: StartCause) {
}
}