1use glutin::dpi::PhysicalPosition;
3use glutin::event::VirtualKeyCode;
4use glutin::event::{ElementState, Event, KeyboardInput, WindowEvent};
5use glutin::event_loop::{ControlFlow, EventLoop};
6use glutin::window::WindowBuilder;
7use glutin::ContextBuilder;
8use glutin::{ContextWrapper, PossiblyCurrent};
9use tracing::*;
10
11use crate::math::Vector2;
12pub use crate::window::window_update_context::WindowUpdateContext;
13use crate::DefaultTelemetry;
14
15const TARGET_FPS: f64 = 60.0;
16const TARGET_FRAME_TIME: f64 = 1000.0 / TARGET_FPS;
17
18#[derive(Default)]
19#[allow(dead_code)]
20pub struct WindowCallbacks {
21 update: Option<Box<dyn FnMut(&mut Box<dyn WindowUserData>, &mut WindowUpdateContext) -> bool>>,
22 fixed_update: Option<Box<dyn FnMut(&mut Box<dyn WindowUserData>, f64)>>,
23 render: Option<Box<dyn FnMut(&mut Box<dyn WindowUserData>)>>,
24}
25
26impl<'a> WindowCallbacks {
27 pub fn with_update(
29 mut self,
30 f: Box<dyn FnMut(&mut Box<dyn WindowUserData>, &mut WindowUpdateContext) -> bool>,
31 ) -> Self {
32 self.update = Some(f);
33 self
34 }
35 pub fn with_fixed_update(
36 mut self,
37 f: Box<dyn FnMut(&mut Box<dyn WindowUserData>, f64)>,
38 ) -> Self {
39 self.fixed_update = Some(f);
40 self
41 }
42 pub fn with_render(mut self, f: Box<dyn FnMut(&mut Box<dyn WindowUserData>)>) -> Self {
43 self.render = Some(f);
44 self
45 }
46}
47
48pub trait WindowUserData {
49 fn as_any(&self) -> &dyn std::any::Any;
50 fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
51}
52
53pub struct Window {
54 el: Option<EventLoop<()>>,
55 windowed_context: Option<ContextWrapper<PossiblyCurrent, glutin::window::Window>>,
56 title: String,
57 pos: Vector2,
58 size: Vector2,
59}
60
61impl Window {
62 pub fn new() -> Self {
63 Self {
64 el: None,
65 windowed_context: None,
66 title: String::new(),
67 pos: Vector2::new(100.0, 100.0),
68 size: Vector2::new(1400.0, 700.0),
69 }
70 }
71
72 pub fn set_title(&mut self, title: &str) {
74 self.title = title.to_string();
75 if let Some(ctx) = &mut self.windowed_context {
77 ctx.window().set_title(&self.title);
78 }
79 }
80
81 pub fn set_position(&mut self, pos: &Vector2) {
82 self.pos = *pos;
83 }
84
85 pub fn set_size(&mut self, size: &Vector2) {
86 self.size = *size;
87 }
88
89 pub fn scale_factor(&self) -> f64 {
90 if let Some(ctx) = &self.windowed_context {
91 ctx.window().scale_factor()
92 } else {
93 1.0
94 }
95 }
96
97 pub fn setup(&mut self) -> anyhow::Result<()> {
98 let el = EventLoop::new();
99 let wb = WindowBuilder::new()
100 .with_inner_size(glutin::dpi::PhysicalSize {
103 width: self.size.x as i32,
104 height: self.size.y as i32,
105 })
106 .with_position(glutin::dpi::PhysicalPosition {
109 x: self.pos.x as i32,
110 y: self.pos.y as i32,
111 })
112 .with_title(&self.title);
113
114 let windowed_context = ContextBuilder::new()
115 .with_vsync(true) .build_windowed(wb, &el)
117 .unwrap();
118
119 let windowed_context = unsafe { windowed_context.make_current().unwrap() };
120
121 println!(
122 "Pixel format of the window's GL context: {:?}",
123 windowed_context.get_pixel_format()
124 );
125
126 self.el = Some(el);
129 self.windowed_context = Some(windowed_context);
130
131 Ok(())
132 }
133
134 pub fn teardown(&mut self) {}
135
136 pub fn get_proc_address(&self, addr: &str) -> *const core::ffi::c_void {
137 match &self.windowed_context {
138 Some(windowed_context) => windowed_context.get_proc_address(addr),
139 None => std::ptr::null(),
140 }
141 }
142
143 fn run_event_loop(
144 mut parent_thread: Option<std::thread::Thread>,
145 el: EventLoop<()>,
146 windowed_context: ContextWrapper<PossiblyCurrent, glutin::window::Window>,
147 mut userdata: Box<dyn WindowUserData>,
148 mut callbacks: WindowCallbacks,
149 ) {
150 let mut is_done = false;
153 let mut window_update_context = WindowUpdateContext::new();
154
155 let mut previous_now = std::time::Instant::now();
157
158 let mut event_count = 0;
159 let mut next_time = std::time::Instant::now();
160
161 let mut slowest_frame_ms = 0.0;
162 let mut slow_frame_count = 0;
163
164 el.run(move |event, _, control_flow| {
165 event_count += 1;
166 let start_time = std::time::Instant::now();
167
168 window_update_context.window_size.x =
169 windowed_context.window().inner_size().width as f32;
170 window_update_context.window_size.y =
171 windowed_context.window().inner_size().height as f32;
172
173 match windowed_context.window().inner_position() {
174 Ok(PhysicalPosition { x, y }) => {
175 let x = x as f32;
176 let y = y as f32;
177 if window_update_context.window_pos.x != x {
178 window_update_context.window_pos.x = x;
179 window_update_context.window_changed = true;
180 }
181 if window_update_context.window_pos.y != y {
182 window_update_context.window_pos.y = y;
183 window_update_context.window_changed = true;
184 }
185 },
186 _ => {},
187 }
188 match event {
206 Event::LoopDestroyed => return,
207 Event::WindowEvent { event, .. } => match event {
208 WindowEvent::Resized(physical_size) => {
209 dbg!(&physical_size);
210 windowed_context.resize(physical_size)
211 },
212 WindowEvent::Moved(physical_size) => {
213 dbg!(&physical_size);
214 },
216 WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
217 WindowEvent::MouseWheel { delta, .. } => {
218 match delta {
220 glutin::event::MouseScrollDelta::LineDelta(x, y) => {
221 window_update_context.mouse_wheel_line_delta.x = x;
222 window_update_context.mouse_wheel_line_delta.y = y;
223 },
224 _ => {},
225 }
226 },
227 WindowEvent::CursorMoved { position, .. } => {
228 let inner_size = windowed_context.window().inner_size();
229
230 let w = inner_size.width as f64;
231 let h = inner_size.height as f64;
232 let mouse_x = position.x / w;
233 let mouse_y = (h - position.y) / h;
234 let scale = 0.5;
235 window_update_context.mouse_pos.x = mouse_x as f32;
236 window_update_context.mouse_pos.y = mouse_y as f32;
237 },
247 WindowEvent::MouseInput { state, button, .. } => {
248 let button_index = match button {
249 glutin::event::MouseButton::Left => 0,
250 glutin::event::MouseButton::Middle => 1,
251 glutin::event::MouseButton::Right => 2,
252 _ => 0,
253 };
254 window_update_context.set_mouse_button(
255 button_index,
256 state == glutin::event::ElementState::Pressed,
257 )
258
259 },
261 WindowEvent::KeyboardInput {
262 input:
263 KeyboardInput {
264 virtual_keycode: Some(virtual_code),
265 state,
266 ..
267 },
268 ..
269 } => match (virtual_code, state) {
270 (VirtualKeyCode::Escape, state) => {
271 window_update_context.is_escape_pressed =
272 state == ElementState::Pressed;
273 },
275 (VirtualKeyCode::Space, state) => {
276 window_update_context.is_space_pressed = state == ElementState::Pressed;
277 },
279 (vkc, state) if vkc >= VirtualKeyCode::A && vkc <= VirtualKeyCode::Z => {
280 let o = ((vkc as u16) - (VirtualKeyCode::A as u16)) as u8;
281 let o = (o + 'a' as u8) as usize;
282 println!("KeyboardInput A-Z {:?} -> {}", &vkc, &o);
283 window_update_context.is_key_pressed[o] =
284 state == ElementState::Pressed;
285 },
286 _ => {
287 if let Some(ascii) = match virtual_code {
289 VirtualKeyCode::Equals => Some(61),
290 VirtualKeyCode::LBracket => Some(91),
291 VirtualKeyCode::Backslash => Some(92),
292 VirtualKeyCode::RBracket => Some(93),
293 VirtualKeyCode::Caret => Some(94),
294 VirtualKeyCode::Slash => Some(47),
295 VirtualKeyCode::Grave => Some(96),
296 _ => None,
297 } {
298 window_update_context.is_key_pressed[ascii] =
299 state == ElementState::Pressed;
300 } else if let Some(fkey) = match virtual_code {
301 VirtualKeyCode::F1 => Some(1),
302 VirtualKeyCode::F2 => Some(2),
303 VirtualKeyCode::F3 => Some(3),
304 VirtualKeyCode::F4 => Some(4),
305 VirtualKeyCode::F5 => Some(5),
306 VirtualKeyCode::F6 => Some(6),
307 VirtualKeyCode::F7 => Some(7),
308 VirtualKeyCode::F8 => Some(8),
309 VirtualKeyCode::F9 => Some(9),
310 VirtualKeyCode::F10 => Some(10),
311 VirtualKeyCode::F11 => Some(11),
312 VirtualKeyCode::F12 => Some(12),
313 _ => None,
314 } {
315 window_update_context.is_function_key_pressed[fkey] =
316 state == ElementState::Pressed;
317 } else {
318 match virtual_code {
319 VirtualKeyCode::LShift | VirtualKeyCode::RShift => {
320 window_update_context.set_modifier_pressed(
321 crate::window::ModifierKey::Shift,
322 state == ElementState::Pressed,
323 );
324 },
325 VirtualKeyCode::LControl | VirtualKeyCode::RControl => {
326 window_update_context.set_modifier_pressed(
327 crate::window::ModifierKey::Ctrl,
328 state == ElementState::Pressed,
329 );
330 },
331 VirtualKeyCode::LAlt | VirtualKeyCode::RAlt => {
332 window_update_context.set_modifier_pressed(
333 crate::window::ModifierKey::Alt,
334 state == ElementState::Pressed,
335 );
336 },
337 vc => {
338 println!("Unmapped KeyboardInput {:?} !", &vc)
339 },
340 };
341 }
342 },
343 },
344 _ => (),
345 },
346 Event::RedrawRequested(_) => {
347 windowed_context.swap_buffers().unwrap();
349 },
350 Event::RedrawEventsCleared => {
351 let now = std::time::Instant::now();
357 let frame_duration = now - previous_now;
359 let time_step = frame_duration.as_secs_f64();
361 previous_now = now;
362 window_update_context.time_step = time_step;
363
364 let done = if let Some(ref mut ucb) = callbacks.update {
365 ucb(&mut userdata, &mut window_update_context)
366 } else {
367 true
368 };
369
370 if !is_done && done {
371 println!("update returned false");
372 *control_flow = ControlFlow::Exit;
373 is_done = true;
374 if let Some(parent_thread) = parent_thread.take() {
375 parent_thread.unpark();
376 }
377 }
378
379 if !is_done {
380 if let Some(ref mut fucb) = callbacks.fixed_update {
381 let half_time_step = 0.5 * time_step;
383 fucb(&mut userdata, half_time_step);
384 fucb(&mut userdata, half_time_step);
385 }
386 }
387
388 if let Some(ref mut rcb) = callbacks.render {
389 rcb(&mut userdata);
390 }
391
392 window_update_context.update();
393 windowed_context.swap_buffers().unwrap();
394 match *control_flow {
395 glutin::event_loop::ControlFlow::Exit => {},
396 _ => {
397 let elapsed_time = std::time::Instant::now()
400 .duration_since(start_time)
401 .as_millis() as f64;
402 let wait_millis = match TARGET_FRAME_TIME >= elapsed_time {
403 true => {
404 DefaultTelemetry::trace::<f64>(
412 "fast frame",
413 elapsed_time / 1000.0,
414 );
415 TARGET_FRAME_TIME - elapsed_time
416 },
417 false => {
418 DefaultTelemetry::trace::<f64>(
419 "slow frame",
420 elapsed_time / 1000.0,
421 );
422 slow_frame_count += 1;
432
433 0.0
434 },
435 };
436 let next_frame_time = std::time::Instant::now()
439 + std::time::Duration::from_millis(wait_millis as u64);
440 *control_flow =
441 glutin::event_loop::ControlFlow::WaitUntil(next_frame_time);
442 next_time = next_frame_time;
443 },
446 }
447 },
448 Event::MainEventsCleared => {
449 },
451 Event::NewEvents(_) => {
452 event_count = 0;
453 },
464 Event::DeviceEvent { .. } => { },
466 e => {
467 println!("Unhandled event: {:?}", e);
468 },
469 }
470 });
473 }
474 pub fn run(
475 &mut self,
476 parent_thread: Option<std::thread::Thread>,
477 userdata: Box<dyn WindowUserData>,
478 callbacks: WindowCallbacks,
479 ) {
480 let el = self.el.take().unwrap();
481 let windowed_context = self.windowed_context.take().unwrap();
482
483 Window::run_event_loop(parent_thread, el, windowed_context, userdata, callbacks);
485 }
486}