1#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
2#![warn(missing_docs)]
3
4pub use pixels;
5pub use pixels::Pixels;
6pub use win_loop::anyhow::{Error, Result};
7pub use win_loop::winit::{
8 self,
9 dpi::PhysicalSize,
10 keyboard::{KeyCode, NamedKey},
11 window::WindowBuilder,
12};
13pub use win_loop::{Context, Duration, Input, InputState};
14
15use pixels::SurfaceTexture;
16use win_loop::winit::{
17 event::{Event, WindowEvent},
18 event_loop::EventLoop,
19 window::WindowId,
20};
21
22pub trait App {
24 fn update(&mut self, ctx: &mut Context) -> Result<()>;
27
28 fn render(&mut self, pix: &mut Pixels, blending_factor: f64) -> Result<()>;
31
32 #[inline]
34 fn handle(&mut self, _event: &Event<()>) -> Result<()> {
35 Ok(())
36 }
37}
38
39#[cfg(not(target_arch = "wasm32"))]
41#[inline]
42pub fn start(
43 window_builder: WindowBuilder,
44 app: impl App + 'static,
45 pixel_buffer_size: PhysicalSize<u32>,
46 target_frame_time: Duration,
47 max_frame_time: Duration,
48) -> Result<()> {
49 use pollster::FutureExt;
50
51 start_async(
52 window_builder,
53 app,
54 pixel_buffer_size,
55 target_frame_time,
56 max_frame_time,
57 )
58 .block_on()
59}
60
61#[inline]
63pub async fn start_async(
64 #[allow(unused_mut)] mut window_builder: WindowBuilder,
65 app: impl App + 'static,
66 pixel_buffer_size: PhysicalSize<u32>,
67 target_frame_time: Duration,
68 max_frame_time: Duration,
69) -> Result<()> {
70 let event_loop = EventLoop::new()?;
71
72 #[cfg(target_arch = "wasm32")]
73 {
74 use winit::platform::web::WindowBuilderExtWebSys;
75
76 window_builder = window_builder.with_append(true);
77 }
78
79 let window = window_builder.build(&event_loop)?;
80 let window_size = window.inner_size();
81
82 let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
83 let pixels = Pixels::new_async(
84 pixel_buffer_size.width,
85 pixel_buffer_size.height,
86 surface_texture,
87 )
88 .await?;
89
90 let app = WinLoopApp {
91 app,
92 win_id: window.id(),
93 resize_order: None,
94 };
95
96 win_loop::start(
97 event_loop,
98 window,
99 app,
100 pixels,
101 target_frame_time,
102 max_frame_time,
103 )
104}
105
106struct WinLoopApp<A: App> {
107 app: A,
108 win_id: WindowId,
109 resize_order: Option<PhysicalSize<u32>>,
110}
111
112impl<A> win_loop::App for WinLoopApp<A>
113where
114 A: App,
115{
116 type RenderContext = Pixels;
117
118 #[inline]
119 fn update(&mut self, ctx: &mut Context) -> Result<()> {
120 self.app.update(ctx)
121 }
122
123 #[inline]
124 fn render(&mut self, ctx: &mut Self::RenderContext, blending_factor: f64) -> Result<()> {
125 if let Some(new_size) = self.resize_order {
126 ctx.resize_surface(new_size.width, new_size.height)?;
127
128 self.resize_order = None;
129 }
130
131 self.app.render(ctx, blending_factor)
132 }
133
134 #[inline]
135 fn handle(&mut self, event: &Event<()>) -> Result<()> {
136 match event {
137 Event::WindowEvent {
138 window_id,
139 event: WindowEvent::Resized(new_size),
140 } if *window_id == self.win_id => {
141 self.resize_order = Some(*new_size);
142 }
143 _ => {}
144 }
145
146 self.app.handle(event)
147 }
148}