1mod game;
48mod runtime;
49mod time;
50
51pub use game::*;
52pub use runtime::*;
53pub use time::*;
54
55use gilrs::Gilrs;
56use optic_core::{log_error, CamProj, Coord2D, OpticResult, Size2D};
57use optic_render::{Camera, GPU};
58use optic_window::{Events, Window};
59use winit::application::ApplicationHandler;
60use winit::event::WindowEvent;
61use winit::event_loop::{ActiveEventLoop, EventLoop};
62use winit::window::WindowId;
63
64pub struct WindowState {
76 pub window: Window,
77 pub events: Events,
78 pub surface_index: usize,
79}
80
81impl WindowState {
82 pub fn new(el: &EventLoop<()>, title: &str, size: Size2D) -> Self {
84 Self {
85 window: Window::new(el, title, size),
86 events: Events::new(),
87 surface_index: 0,
88 }
89 }
90
91 pub fn close(&mut self) {
93 self.window.close();
94 }
95
96 pub fn is_closed(&self) -> bool {
98 self.window.is_closed()
99 }
100
101 pub fn surface_index(&self) -> usize {
103 self.surface_index
104 }
105}
106
107pub struct FrameState<'a> {
112 pub time: &'a Time,
113 pub windows: &'a mut [WindowState],
114 pub gpu: &'a mut GPU,
115 pub camera: &'a mut Camera,
116}
117
118pub struct GameLoop<F: FnMut(&mut FrameState)> {
144 event_loop: Option<EventLoop<()>>,
145 windows: Vec<WindowState>,
146 gpu: Option<GPU>,
147 camera: Camera,
148 time: Time,
149 gilrs: Gilrs,
150 frame_fn: F,
151}
152
153impl<F: FnMut(&mut FrameState)> GameLoop<F> {
154 pub fn new(
164 el: EventLoop<()>,
165 mut gpu: GPU,
166 camera: Camera,
167 mut windows: Vec<WindowState>,
168 frame_fn: F,
169 ) -> OpticResult<Self> {
170 for ws in windows.iter_mut() {
171 if let Some(handle) = ws.window.raw_handle() {
172 let size = ws.window.size();
173 let idx = gpu.ctx.attach_window(handle, size)
174 .map_err(|e| optic_core::OpticError::custom(&format!("attach window failed: {e}")))?;
175 ws.surface_index = idx;
176 }
177 }
178
179 let gilrs = Gilrs::new()
180 .map_err(|e| optic_core::OpticError::custom(&format!("gilrs init failed: {e}")))?;
181
182 Ok(Self {
183 event_loop: Some(el),
184 windows,
185 gpu: Some(gpu),
186 camera,
187 time: Time::new(),
188 gilrs,
189 frame_fn,
190 })
191 }
192
193 pub fn run(mut self) {
198 let el = self.event_loop.take().unwrap();
199 let _ = el.run_app(&mut self);
200 }
201}
202
203impl<F: FnMut(&mut FrameState)> ApplicationHandler for GameLoop<F> {
204 fn resumed(&mut self, _el: &ActiveEventLoop) {
205 self.time.start_time = std::time::Instant::now();
206 self.time.prev_time = std::time::Instant::now();
207 self.time.prev_sec = std::time::Instant::now();
208 }
209
210 fn window_event(
211 &mut self,
212 _el: &ActiveEventLoop,
213 id: WindowId,
214 event: WindowEvent,
215 ) {
216 for ws in &mut self.windows {
217 if !ws.window.is_running() { continue; }
218 if ws.window.id().map_or(true, |wid| wid != id) { continue; }
219
220 match &event {
221 WindowEvent::Resized(_size) => {
222 if let Some(gpu) = &mut self.gpu {
223 gpu.ctx.resize_window(ws.surface_index, ws.window.size());
224 let _ = gpu.ctx.make_current(ws.surface_index);
225 self.camera.set_size(ws.window.size());
226 }
227 }
228 WindowEvent::CursorMoved { position, .. } => {
229 ws.window.notify_cursor_moved(Coord2D::from(position.x, position.y));
230 }
231 WindowEvent::CursorEntered { .. } => {
232 ws.window.notify_cursor_inside(true);
233 }
234 WindowEvent::CursorLeft { .. } => {
235 ws.window.notify_cursor_inside(false);
236 }
237 WindowEvent::CloseRequested => {
238 ws.events.close_requested = true;
239 }
240 _ => {}
241 }
242 ws.events.process_window_event(&event, &ws.window);
243 break;
244 }
245 }
246
247 fn about_to_wait(&mut self, _el: &ActiveEventLoop) {
248 let gpu = match &mut self.gpu {
249 Some(g) => g,
250 None => return,
251 };
252
253 self.windows.retain(|ws| !ws.window.is_closed());
254
255 if self.windows.is_empty() {
256 return;
257 }
258
259 while let Some(gilrs_event) = self.gilrs.next_event() {
260 for ws in &mut self.windows {
261 ws.events.process_gilrs_event(&gilrs_event);
262 }
263 }
264
265 self.time.update();
266
267 {
268 let mut frame = FrameState {
269 time: &self.time,
270 windows: &mut self.windows,
271 gpu,
272 camera: &mut self.camera,
273 };
274 (self.frame_fn)(&mut frame);
275 }
276
277 for ws in &mut self.windows {
278 ws.events.end_frame();
279 }
280
281 for ws in &mut self.windows {
282 ws.window.request_redraw();
283 }
284 }
285}
286
287pub fn run<F>(title: &str, size: Size2D, frame_fn: F)
302where
303 F: FnMut(&mut FrameState) + 'static,
304{
305 let result = try_run(title, size, frame_fn);
306 if let Err(e) = result {
307 log_error!("{}", e);
308 optic_core::end(optic_core::ERROR);
309 }
310}
311
312fn try_run<F>(title: &str, size: Size2D, frame_fn: F) -> OpticResult<()>
313where
314 F: FnMut(&mut FrameState) + 'static,
315{
316 let el = EventLoop::new()
317 .map_err(|e| optic_core::OpticError::custom(&format!("event loop creation failed: {e}")))?;
318 let ws = WindowState::new(&el, title, size);
319 let handle = ws.window.raw_handle()
320 .ok_or_else(|| optic_core::OpticError::custom("failed to get raw window handle"))?;
321 let display_handle = ws.window.raw_display_handle()
322 .ok_or_else(|| optic_core::OpticError::custom("failed to get raw display handle"))?;
323 let gpu = GPU::new_windowed(handle, display_handle, ws.window.size())
324 .map_err(|e| optic_core::OpticError::custom(&format!("gpu init failed: {e}")))?;
325 let camera = Camera::new(ws.window.size(), CamProj::Persp);
326 let game = GameLoop::new(el, gpu, camera, vec![ws], frame_fn)?;
327 game.run();
328 Ok(())
329}