1use std::sync::atomic::{AtomicBool, Ordering};
2use std::sync::Arc;
3use std::time::{Duration, Instant};
4
5use kludgine_core::easygpu::prelude::*;
6use kludgine_core::figures::num_traits::One;
7use kludgine_core::figures::{DisplayScale, Displayable, Pixels};
8use kludgine_core::math::{Point, Scale, Size};
9use kludgine_core::scene::{Scene, SceneEvent};
10use kludgine_core::winit::dpi::{PhysicalPosition, PhysicalSize};
11use kludgine_core::winit::event::WindowEvent as WinitWindowEvent;
12use kludgine_core::winit::window::{Theme, WindowId, WindowLevel};
13use kludgine_core::winit::{self};
14use kludgine_core::{flume, FrameRenderer};
15use once_cell::sync::OnceCell;
16use tracing::instrument;
17
18use super::OpenWindow;
19use crate::runtime::{Runtime, RuntimeRequest, WINDOWS};
20use crate::window::event::{ElementState, Event, InputEvent, VirtualKeyCode, WindowEvent};
21use crate::window::{CloseResponse, Window, WindowMessage, WINDOW_CHANNELS};
22use crate::Error;
23
24pub struct RuntimeWindow {
25 pub window_id: WindowId,
26 pub keep_running: Arc<AtomicBool>,
27 receiver: flume::Receiver<WindowMessage>,
28 event_sender: flume::Sender<WindowEvent>,
29 last_known_size: Size<u32, Pixels>,
30 last_known_scale_factor: DisplayScale<f32>,
31}
32
33pub struct RuntimeWindowConfig {
34 window_id: WindowId,
35 instance: wgpu::Instance,
36 surface: wgpu::Surface,
37 initial_size: Size<u32, Pixels>,
38 scale_factor: f32,
39}
40
41impl RuntimeWindowConfig {
42 pub fn new(window: &winit::window::Window) -> Result<Self, wgpu::CreateSurfaceError> {
43 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
46 backends: wgpu::Backends::PRIMARY,
47 dx12_shader_compiler: wgpu::Dx12Compiler::default(),
48 });
49 let surface = unsafe { instance.create_surface(window) }?;
50 Ok(Self {
51 window_id: window.id(),
52 instance,
53 surface,
54 initial_size: Size::new(window.inner_size().width, window.inner_size().height),
55 scale_factor: window.scale_factor() as f32,
56 })
57 }
58}
59
60static OPENED_FIRST_WINDOW: OnceCell<()> = OnceCell::new();
61
62pub fn opened_first_window() -> bool {
63 OPENED_FIRST_WINDOW.get().is_some()
64}
65
66fn set_opened_first_window() {
67 OPENED_FIRST_WINDOW.get_or_init(|| ());
68}
69
70#[cfg(not(target_arch = "wasm32"))]
71type Format = kludgine_core::sprite::Srgb;
72#[cfg(target_arch = "wasm32")]
73type Format = kludgine_core::sprite::Normal;
74
75impl RuntimeWindow {
76 pub(crate) fn open<T>(
77 window_receiver: &flume::Receiver<RuntimeWindowConfig>,
78 initial_system_theme: Theme,
79 app_window: T,
80 ) where
81 T: Window + Sized + 'static,
82 {
83 let RuntimeWindowConfig {
84 window_id,
85 instance,
86 surface,
87 initial_size,
88 scale_factor,
89 } = window_receiver
90 .recv()
91 .expect("Error receiving winit::window");
92
93 let (message_sender, message_receiver) = flume::unbounded();
94 let (event_sender, event_receiver) = flume::unbounded();
95
96 let keep_running = Arc::new(AtomicBool::new(true));
97 let task_keep_running = keep_running.clone();
98 let window_event_sender = event_sender.clone();
99 let (scene_event_sender, scene_event_receiver) = flume::unbounded();
100
101 std::thread::Builder::new()
104 .name(String::from("kludgine-window-loop"))
105 .spawn(move || {
106 Self::window_main(
107 window_id,
108 scene_event_sender,
109 window_event_sender,
110 event_receiver,
111 initial_system_theme,
112 app_window,
113 );
114 })
115 .unwrap();
116
117 let renderer = Runtime::block_on(async move {
118 Renderer::for_surface(surface, &instance, 4)
119 .await
120 .expect("Error creating renderer for window")
121 });
122
123 FrameRenderer::<Format>::run(renderer, task_keep_running, scene_event_receiver, || {
124 RuntimeRequest::WindowClosed.send();
125 });
126
127 {
128 let mut channels = WINDOW_CHANNELS.lock().unwrap();
129 channels.insert(window_id, message_sender);
130 }
131
132 let mut runtime_window = Self {
133 receiver: message_receiver,
134 last_known_size: initial_size,
135 keep_running,
136 event_sender,
137 last_known_scale_factor: DisplayScale::new(Scale::new(scale_factor), Scale::one()),
138 window_id,
139 };
140 runtime_window.notify_size_changed();
141
142 let mut windows = WINDOWS.write();
143 windows.insert(window_id, runtime_window);
144
145 set_opened_first_window();
146 }
147
148 fn request_window_close<T>(id: WindowId, window: &mut OpenWindow<T>) -> crate::Result<bool>
149 where
150 T: Window,
151 {
152 if let CloseResponse::RemainOpen = window.request_close()? {
153 return Ok(false);
154 }
155
156 WindowMessage::Close.send_to(id)?;
157 Ok(true)
158 }
159
160 fn next_window_event_non_blocking(
161 event_receiver: &mut flume::Receiver<WindowEvent>,
162 ) -> crate::Result<Option<WindowEvent>> {
163 match event_receiver.try_recv() {
164 Ok(event) => Ok(Some(event)),
165 Err(flume::TryRecvError::Empty) => Ok(None),
166 Err(flume::TryRecvError::Disconnected) => Err(Error::InternalWindowMessageSend(
167 "Window channel closed".to_owned(),
168 )),
169 }
170 }
171
172 fn next_window_event_blocking(
173 event_receiver: &mut flume::Receiver<WindowEvent>,
174 ) -> crate::Result<Option<WindowEvent>> {
175 match event_receiver.recv() {
176 Ok(event) => Ok(Some(event)),
177 Err(_) => Err(Error::InternalWindowMessageSend(
178 "Window channel closed".to_owned(),
179 )),
180 }
181 }
182
183 fn next_window_event_timeout(
184 event_receiver: &mut flume::Receiver<WindowEvent>,
185 wait_for: Duration,
186 ) -> crate::Result<Option<WindowEvent>> {
187 match event_receiver.recv_timeout(wait_for) {
188 Ok(event) => Ok(Some(event)),
189 Err(flume::RecvTimeoutError::Timeout) => Ok(None),
190 Err(_) => Err(Error::InternalWindowMessageSend(
191 "Window channel closed".to_owned(),
192 )),
193 }
194 }
195
196 fn next_window_event<T>(
197 event_receiver: &mut flume::Receiver<WindowEvent>,
198 window: &OpenWindow<T>,
199 ) -> crate::Result<Option<WindowEvent>>
200 where
201 T: Window,
202 {
203 #[cfg(target_arch = "wasm32")]
204 {
205 Self::next_window_event_non_blocking(event_receiver)
211 }
212
213 #[cfg(not(target_arch = "wasm32"))]
214 {
215 let next_redraw_target = window.next_redraw_target();
216 if !window.can_wait_for_events() {
217 Self::next_window_event_non_blocking(event_receiver)
218 } else if let Some(redraw_at) = next_redraw_target.next_update_instant() {
219 let timeout_target = redraw_at.timeout_target();
220 let remaining_time =
221 timeout_target.and_then(|t| t.checked_duration_since(Instant::now()));
222 if let Some(remaining_time) = remaining_time {
223 Self::next_window_event_timeout(event_receiver, remaining_time)
224 } else {
225 Self::next_window_event_non_blocking(event_receiver)
227 }
228 } else {
229 Self::next_window_event_blocking(event_receiver)
230 }
231 }
232 }
233
234 fn window_loop<T>(
235 id: WindowId,
236 scene_event_sender: flume::Sender<SceneEvent>,
237 event_sender: flume::Sender<WindowEvent>,
238 mut event_receiver: flume::Receiver<WindowEvent>,
239 initial_system_theme: Theme,
240 window: T,
241 ) -> crate::Result<()>
242 where
243 T: Window,
244 {
245 let scene = Scene::new(scene_event_sender, initial_system_theme);
246
247 let target_fps = window.target_fps();
248 let mut window = OpenWindow::new(window, id, event_sender, scene);
249
250 window.initialize()?;
251 loop {
252 while let Some(event) = match Self::next_window_event(&mut event_receiver, &window) {
253 Ok(event) => event,
254 Err(_) => return Ok(()),
255 } {
256 match event {
257 WindowEvent::WakeUp | WindowEvent::RedrawRequested => {
258 window.redraw_status.set_needs_redraw();
259 }
260 WindowEvent::SystemThemeChanged(system_theme) => {
261 window.scene_mut().set_system_theme(system_theme);
262 window.redraw_status.set_needs_redraw();
263 }
264 WindowEvent::Resize { size, scale_factor } => {
265 window.scene_mut().set_size(size.cast_unit());
266 window.scene_mut().set_dpi_scale(scale_factor);
267 window.redraw_status.set_needs_redraw();
268 }
269 WindowEvent::CloseRequested => {
270 if Self::request_window_close(id, &mut window)? {
271 return Ok(());
272 }
273 }
274 WindowEvent::Input(input) => {
275 if let Event::Keyboard {
276 key: Some(key),
277 state,
278 ..
279 } = input.event
280 {
281 match state {
282 ElementState::Pressed => {
283 window.scene_mut().keys_pressed.insert(key);
284 }
285 ElementState::Released => {
286 window.scene_mut().keys_pressed.remove(&key);
287 }
288 }
289 }
290
291 window.process_input(input)?;
292 }
293 WindowEvent::ReceiveCharacter(character) => {
294 window.receive_character(character)?;
295 }
296 }
297 }
298
299 {
301 let modifiers = window.scene().modifiers_pressed();
302 if modifiers.primary_modifier()
303 && window.scene().keys_pressed.contains(&VirtualKeyCode::W)
304 && Self::request_window_close(id, &mut window)?
305 {
306 return Ok(());
307 }
308 }
309
310 if window.scene().size().area().get() > 0.0 {
311 let additional_scale = window.additional_scale();
312 if additional_scale != window.scene().scale().additional_scale() {
313 window.scene_mut().set_additional_scale(additional_scale);
314 WindowMessage::SetAdditionalScale(additional_scale).send_to(id)?;
315 }
316
317 window.scene_mut().start_frame();
318
319 window.update(target_fps)?;
320
321 if window.should_redraw_now() {
322 window.render()?;
323 window.scene_mut().end_frame();
324 }
325 }
326 }
327 }
328
329 fn window_main<T>(
330 id: WindowId,
331 scene_event_sender: flume::Sender<SceneEvent>,
332 event_sender: flume::Sender<WindowEvent>,
333 event_receiver: flume::Receiver<WindowEvent>,
334 initial_system_theme: Theme,
335 window: T,
336 ) where
337 T: Window,
338 {
339 Self::window_loop(
340 id,
341 scene_event_sender,
342 event_sender,
343 event_receiver,
344 initial_system_theme,
345 window,
346 )
347 .expect("Error running window loop.");
348 }
349
350 pub(crate) fn count() -> usize {
351 if opened_first_window() {
352 let channels = WINDOW_CHANNELS.lock().unwrap();
353 channels.len()
354 } else {
355 1
359 }
360 }
361
362 pub(crate) fn receive_messages(&mut self) {
363 while let Ok(request) = self.receiver.try_recv() {
364 match request {
365 WindowMessage::RequestClose => {
366 let _: Result<_, _> = self.event_sender.send(WindowEvent::CloseRequested);
367 }
368 WindowMessage::Close => {
369 let mut channels = WINDOW_CHANNELS.lock().unwrap();
370 channels.remove(&self.window_id);
371 self.keep_running.store(false, Ordering::SeqCst);
372 }
373 WindowMessage::SetAdditionalScale(scale) => {
374 self.last_known_scale_factor.set_additional_scale(scale);
375 }
376 }
377 }
378 }
379
380 pub(crate) fn request_redraw(&self) {
381 self.event_sender
382 .try_send(WindowEvent::RedrawRequested)
383 .unwrap_or_default();
384 }
385
386 #[instrument(name = "RuntimeWindow::process_event", level = "trace", skip(self))]
387 pub(crate) fn process_event(&mut self, event: &WinitWindowEvent<'_>) {
388 match event {
389 WinitWindowEvent::CloseRequested => {
390 self.event_sender
391 .try_send(WindowEvent::CloseRequested)
392 .unwrap_or_default();
393 }
394 WinitWindowEvent::Resized(size) => {
395 self.last_known_size = Size::new(size.width, size.height);
396 self.notify_size_changed();
397 }
398 WinitWindowEvent::ScaleFactorChanged {
399 scale_factor,
400 new_inner_size,
401 } => {
402 self.last_known_scale_factor
403 .set_dpi_scale(Scale::new(*scale_factor as f32));
404 self.last_known_size = Size::new(new_inner_size.width, new_inner_size.height);
405 self.notify_size_changed();
406 }
407 WinitWindowEvent::KeyboardInput {
408 device_id, input, ..
409 } => self
410 .event_sender
411 .try_send(WindowEvent::Input(InputEvent {
412 device_id: *device_id,
413 event: Event::Keyboard {
414 key: input.virtual_keycode,
415 state: input.state,
416 scancode: input.scancode,
417 },
418 }))
419 .unwrap_or_default(),
420 WinitWindowEvent::MouseInput {
421 device_id,
422 button,
423 state,
424 ..
425 } => self
426 .event_sender
427 .try_send(WindowEvent::Input(InputEvent {
428 device_id: *device_id,
429 event: Event::MouseButton {
430 button: *button,
431 state: *state,
432 },
433 }))
434 .unwrap_or_default(),
435 WinitWindowEvent::MouseWheel {
436 device_id,
437 delta,
438 phase,
439 ..
440 } => self
441 .event_sender
442 .try_send(WindowEvent::Input(InputEvent {
443 device_id: *device_id,
444 event: Event::MouseWheel {
445 delta: *delta,
446 touch_phase: *phase,
447 },
448 }))
449 .unwrap_or_default(),
450 WinitWindowEvent::CursorMoved {
451 device_id,
452 position,
453 ..
454 } => self
455 .event_sender
456 .try_send(WindowEvent::Input(InputEvent {
457 device_id: *device_id,
458 event: Event::MouseMoved {
459 position: Some(
460 Point::<f32, Pixels>::new(position.x as f32, position.y as f32)
461 .to_scaled(&self.last_known_scale_factor),
462 ),
463 },
464 }))
465 .unwrap_or_default(),
466 WinitWindowEvent::CursorLeft { device_id } => self
467 .event_sender
468 .try_send(WindowEvent::Input(InputEvent {
469 device_id: *device_id,
470 event: Event::MouseMoved { position: None },
471 }))
472 .unwrap_or_default(),
473 WinitWindowEvent::ReceivedCharacter(character) => self
474 .event_sender
475 .try_send(WindowEvent::ReceiveCharacter(*character))
476 .unwrap_or_default(),
477 WinitWindowEvent::ThemeChanged(theme) => self
478 .event_sender
479 .try_send(WindowEvent::SystemThemeChanged(*theme))
480 .unwrap_or_default(),
481 _ => {}
482 }
483 }
484
485 fn notify_size_changed(&mut self) {
486 self.event_sender
487 .try_send(WindowEvent::Resize {
488 size: self.last_known_size,
489 scale_factor: self.last_known_scale_factor.dpi_scale(),
490 })
491 .unwrap_or_default();
492 }
493}
494
495#[derive(Clone, Copy, Debug)]
497pub struct WindowHandle(pub WindowId);
498
499impl WindowHandle {
500 pub fn set_title(&self, title: &str) {
502 if let Some(window) = Runtime::winit_window(self.0) {
503 window.set_title(title);
504 }
505 }
506
507 #[must_use]
509 pub fn inner_size(&self) -> Size<u32, Pixels> {
510 Runtime::winit_window(self.0)
511 .map(|window| Size::new(window.inner_size().width, window.inner_size().height))
512 .unwrap_or_default()
513 }
514
515 pub fn set_inner_size(&self, new_size: Size<u32, Pixels>) {
517 if let Some(window) = Runtime::winit_window(self.0) {
518 window.set_inner_size(PhysicalSize::new(new_size.width, new_size.height));
519 }
520 }
521
522 #[must_use]
525 pub fn outer_position(&self) -> Point<i32, Pixels> {
526 Runtime::winit_window(self.0)
527 .and_then(|window| window.outer_position().ok())
528 .map(|position| Point::new(position.x, position.y))
529 .unwrap_or_default()
530 }
531
532 pub fn set_outer_position(&self, new_position: Point<i32, Pixels>) {
534 if let Some(window) = Runtime::winit_window(self.0) {
535 window.set_outer_position(PhysicalPosition::new(new_position.x, new_position.y));
536 }
537 }
538
539 #[must_use]
541 pub fn inner_position(&self) -> Point<i32, Pixels> {
542 Runtime::winit_window(self.0)
543 .and_then(|window| window.inner_position().ok())
544 .map(|position| Point::new(position.x, position.y))
545 .unwrap_or_default()
546 }
547
548 pub fn set_window_level(&self, level: WindowLevel) {
550 if let Some(window) = Runtime::winit_window(self.0) {
551 window.set_window_level(level);
552 }
553 }
554
555 #[must_use]
557 pub fn maximized(&self) -> bool {
558 if let Some(window) = Runtime::winit_window(self.0) {
559 window.is_maximized()
560 } else {
561 false
562 }
563 }
564
565 pub fn set_maximized(&self, maximized: bool) {
567 if let Some(window) = Runtime::winit_window(self.0) {
568 window.set_maximized(maximized);
569 }
570 }
571
572 pub fn set_minimized(&self, minimized: bool) {
574 if let Some(window) = Runtime::winit_window(self.0) {
575 window.set_minimized(minimized);
576 }
577 }
578
579 pub fn request_close(&self) {
581 drop(WindowMessage::RequestClose.send_to(self.0));
582 }
583}