1
use std::sync::atomic::{AtomicBool, Ordering};
2
use std::sync::Arc;
3
use std::time::{Duration, Instant};
4

            
5
use kludgine_core::easygpu::prelude::*;
6
use kludgine_core::figures::num_traits::One;
7
use kludgine_core::figures::{DisplayScale, Displayable, Pixels};
8
use kludgine_core::math::{Point, Scale, Size};
9
use kludgine_core::scene::{Scene, SceneEvent};
10
use kludgine_core::winit::dpi::{PhysicalPosition, PhysicalSize};
11
use kludgine_core::winit::event::WindowEvent as WinitWindowEvent;
12
use kludgine_core::winit::window::{Theme, WindowId, WindowLevel};
13
use kludgine_core::winit::{self};
14
use kludgine_core::{flume, FrameRenderer};
15
use once_cell::sync::OnceCell;
16
use tracing::instrument;
17

            
18
use super::OpenWindow;
19
use crate::runtime::{Runtime, RuntimeRequest, WINDOWS};
20
use crate::window::event::{ElementState, Event, InputEvent, VirtualKeyCode, WindowEvent};
21
use crate::window::{CloseResponse, Window, WindowMessage, WINDOW_CHANNELS};
22
use crate::Error;
23

            
24
pub 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

            
33
pub 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

            
41
impl RuntimeWindowConfig {
42
    pub fn new(window: &winit::window::Window) -> Result<Self, wgpu::CreateSurfaceError> {
43
        // TODO in wasm, we need to explicity enable GL, but since wasm isn't possible
44
        // right now, we're just hardcoding primary
45
        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

            
60
static OPENED_FIRST_WINDOW: OnceCell<()> = OnceCell::new();
61

            
62
pub fn opened_first_window() -> bool {
63
    OPENED_FIRST_WINDOW.get().is_some()
64
}
65

            
66
fn set_opened_first_window() {
67
    OPENED_FIRST_WINDOW.get_or_init(|| ());
68
}
69

            
70
#[cfg(not(target_arch = "wasm32"))]
71
type Format = kludgine_core::sprite::Srgb;
72
#[cfg(target_arch = "wasm32")]
73
type Format = kludgine_core::sprite::Normal;
74

            
75
impl 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
        // Each window has its own thread/task operating its core update/render
102
        // lifecycle.
103
        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
            // On wasm, the browser is controlling our async runtime, and we don't have a
206
            // good way to do a Timeout-style function This shouldn't have any noticable
207
            // effects, because the browser will throttle frames for us automatically
208
            // We could refactor to trigger redraws separately from winit, in which case we
209
            // could properly use the frame update logic
210
            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
                    // No remaining time, only process events that have already arrived.
226
                    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
            // Check for Cmd + W or Alt + f4 to close the window.
300
            {
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
            // If our first window hasn't opened, return a count of 1. This will happen in a
356
            // single-threaded environment because RuntimeWindow::open is spawned, not
357
            // blocked_on.
358
            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
/// A handle to an open window.
496
#[derive(Clone, Copy, Debug)]
497
pub struct WindowHandle(pub WindowId);
498

            
499
impl WindowHandle {
500
    /// Sets the window title.
501
    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
    /// Returns the size of the content area of the window.
508
    #[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
    /// Attempts to resize the window to `new_size`. This may not work on all platforms.
516
    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
    /// Returns the position on the screen of the window's top-left corner. On
523
    /// platforms where this is unsupported, `innner_position()` is returned.
524
    #[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
    /// Sets the outer position of the window. This may not work on all platforms.
533
    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
    /// Returns the position of the top-left of the content area in screen coordinates.
540
    #[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
    /// Sets the window level.
549
    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
    /// Returns true if the window is maximized.
556
    #[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
    /// Sets whether the window should be maximized.
566
    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
    /// Sets whether the window should be minimized.
573
    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
    /// Requests that the window close.
580
    pub fn request_close(&self) {
581
        drop(WindowMessage::RequestClose.send_to(self.0));
582
    }
583
}