#![deny(missing_docs)]
use std::collections::HashMap;
#[cfg(feature = "wgpu")]
use std::sync::Arc;
use std::time::Duration;
pub use gapp::{Render, Update};
#[cfg(feature = "opengl")]
#[cfg(not(target_arch = "wasm32"))]
use glutin::{
context::PossiblyCurrentContext,
prelude::*,
surface::{Surface, WindowSurface},
};
use instant::Instant;
#[cfg(not(target_arch = "wasm32"))]
use winit::dpi::PhysicalSize;
use winit::{
event::{Event, WindowEvent},
event_loop::{EventLoop, EventLoopWindowTarget},
window::{Window, WindowId},
};
pub trait WindowInput<C, R> {
fn input<T>(
&mut self,
window_id: WindowId,
event: &WindowEvent,
event_loop: &EventLoopWindowTarget<T>,
context: &mut C,
windows: &mut Windows<R>,
);
}
#[cfg(feature = "opengl")]
pub struct WindowData {
pub window: Window,
#[cfg(not(target_arch = "wasm32"))]
pub context: PossiblyCurrentContext,
#[cfg(not(target_arch = "wasm32"))]
pub surface: Surface<WindowSurface>,
}
#[cfg(feature = "wgpu")]
pub struct WindowData {
pub device: wgpu::Device,
pub queue: wgpu::Queue,
pub surface_config: wgpu::SurfaceConfiguration,
pub surface: wgpu::Surface<'static>,
pub window: Arc<Window>,
}
pub struct Windows<R> {
map: HashMap<WindowId, (WindowData, R)>,
}
impl<R> Windows<R> {
pub fn new() -> Self {
Self {
map: HashMap::new(),
}
}
pub fn insert(&mut self, window_data: WindowData, renderer: R) -> WindowId {
let id = window_data.window.id();
self.map.insert(id, (window_data, renderer));
id
}
pub fn remove(&mut self, id: &WindowId) -> Option<(WindowData, R)> {
self.map.remove(id)
}
pub fn get(&self, id: &WindowId) -> Option<&(WindowData, R)> {
self.map.get(id)
}
pub fn get_mut(&mut self, id: &WindowId) -> Option<&mut (WindowData, R)> {
self.map.get_mut(id)
}
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
pub fn len(&self) -> usize {
self.map.len()
}
pub fn iter(&self) -> impl Iterator<Item = (&WindowId, &(WindowData, R))> {
self.map.iter()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&WindowId, &mut (WindowData, R))> {
self.map.iter_mut()
}
}
impl<R> Default for Windows<R> {
fn default() -> Self {
Self::new()
}
}
pub trait Present<R> {
fn present(&self, renderer: &mut R, window_data: &WindowData);
}
pub fn run<
I: 'static,
R: 'static,
T,
E: WindowInput<I, R> + Present<R> + Render<R> + Update + 'static,
>(
mut application: E,
event_loop: EventLoop<T>,
fps: u64,
windows: Windows<R>,
mut input_context: I,
) {
let mut windows = windows;
let timestep = Duration::from_nanos(1000000000 / fps);
let start = Instant::now();
let mut prev_time = start;
let _ = event_loop.run(move |event, event_loop| match event {
Event::WindowEvent {
ref event,
window_id,
} => {
if let Some((window_data, renderer)) = windows.get_mut(&window_id) {
match event {
#[cfg(not(target_arch = "wasm32"))]
&WindowEvent::Resized(PhysicalSize { width, height }) => {
let width = width.max(1);
let height = height.max(1);
#[cfg(feature = "opengl")]
window_data.surface.resize(
&window_data.context,
width.try_into().unwrap(),
height.try_into().unwrap(),
);
#[cfg(feature = "wgpu")]
{
window_data.surface_config.width = width;
window_data.surface_config.height = height;
window_data
.surface
.configure(&window_data.device, &window_data.surface_config);
}
}
WindowEvent::RedrawRequested => {
application.render(renderer);
application.present(renderer, window_data);
}
_ => (),
}
}
application.input(
window_id,
event,
event_loop,
&mut input_context,
&mut windows,
);
}
Event::AboutToWait => {
#[allow(unused_variables)]
let frame_duration = prev_time.elapsed();
application.update(timestep.as_secs_f32());
#[cfg(not(target_arch = "wasm32"))]
if frame_duration < timestep {
std::thread::sleep(timestep - frame_duration);
}
for (_, (window_data, _)) in windows.iter() {
window_data.window.request_redraw();
}
prev_time = Instant::now();
}
_ => (),
});
}