1
use std::sync::Arc;
2
use std::time::{Duration, Instant};
3

            
4
use kludgine_core::figures::{Points, Scaled};
5
use kludgine_core::flume::Sender;
6
use kludgine_core::math::Scale;
7
use kludgine_core::scene::{Scene, Target};
8
use kludgine_core::winit::window::WindowId;
9

            
10
use super::event::{InputEvent, WindowEvent};
11
use super::{CloseResponse, Window};
12
use crate::WindowHandle;
13

            
14
pub struct OpenWindow<T: Window> {
15
    window: T,
16
    window_id: WindowId,
17
    pub(crate) redraw_status: RedrawStatus,
18
    scene: Arc<Scene>,
19
}
20

            
21
/// Allows requesting window refreshes outside of the event loop.
22
#[derive(Clone, Debug)]
23
pub struct RedrawRequester {
24
    event_sender: Sender<WindowEvent>,
25
}
26

            
27
impl RedrawRequester {
28
    /// Requests the window refresh itself. This will trigger [`Window::update`]
29
    /// before rendering.
30
    pub fn request_redraw(&self) {
31
        let _: Result<_, _> = self.event_sender.send(WindowEvent::RedrawRequested);
32
    }
33

            
34
    /// Wakes the event loop, without necessarily redrawing.
35
    pub fn awaken(&self) {
36
        let _: Result<_, _> = self.event_sender.send(WindowEvent::WakeUp);
37
    }
38
}
39

            
40
/// Tracks when a window should be redrawn. Allows for rendering a frame
41
/// immediately as well as scheduling a refresh in the future.
42
#[derive(Debug)]
43
pub struct RedrawStatus {
44
    next_redraw_target: RedrawTarget,
45
    needs_render: bool,
46
    needs_update: bool,
47
    event_sender: Sender<WindowEvent>,
48
}
49

            
50
impl RedrawStatus {
51
    /// Triggers a redraw as soon as possible. Any estimated frame instants will
52
    /// be ignored.
53
    pub fn set_needs_redraw(&mut self) {
54
        if !self.needs_render {
55
            self.needs_render = true;
56
            let _: Result<_, _> = self.event_sender.send(WindowEvent::WakeUp);
57
        }
58
    }
59

            
60
    /// Estimates the next redraw instant by adding `duration` to
61
    /// `Instant::now()`. If this is later than the current estimate, it
62
    /// will be ignored.
63
    pub fn estimate_next_frame(&mut self, duration: Duration) {
64
        self.estimate_next_frame_instant(Instant::now().checked_add(duration).unwrap());
65
    }
66

            
67
    /// Estimates the next redraw instant. If `instant` is later than the
68
    /// current estimate, it will be ignored.
69
    pub fn estimate_next_frame_instant(&mut self, instant: Instant) {
70
        match self.next_redraw_target {
71
            RedrawTarget::Never | RedrawTarget::None => {
72
                self.next_redraw_target = RedrawTarget::Scheduled(instant);
73
            }
74
            RedrawTarget::Scheduled(existing_instant) => {
75
                if instant < existing_instant {
76
                    self.next_redraw_target = RedrawTarget::Scheduled(instant);
77
                }
78
            }
79
        }
80
    }
81

            
82
    /// Returns a redraw requester that can be used outside of the event loop.
83
    #[must_use]
84
    pub fn redraw_requester(&self) -> RedrawRequester {
85
        RedrawRequester {
86
            event_sender: self.event_sender.clone(),
87
        }
88
    }
89
}
90

            
91
impl<T: Window> OpenWindow<T> {
92
    pub(crate) fn new(
93
        window: T,
94
        window_id: WindowId,
95
        event_sender: Sender<WindowEvent>,
96
        scene: Scene,
97
    ) -> Self {
98
        Self {
99
            window,
100
            window_id,
101
            scene: Arc::new(scene),
102
            redraw_status: RedrawStatus {
103
                needs_render: true,
104
                needs_update: true,
105
                next_redraw_target: RedrawTarget::None,
106
                event_sender,
107
            },
108
        }
109
    }
110

            
111
    pub(crate) fn clear_redraw_target(&mut self) {
112
        self.redraw_status.needs_render = false;
113
        self.redraw_status.next_redraw_target = RedrawTarget::None;
114
    }
115

            
116
    pub(crate) fn set_has_updated(&mut self) {
117
        self.redraw_status.needs_update = false;
118
    }
119

            
120
    pub(crate) fn initialize_redraw_target(&mut self, target_fps: Option<u16>) {
121
        if let RedrawTarget::None = self.redraw_status.next_redraw_target {
122
            match target_fps {
123
                Some(fps) => {
124
                    self.redraw_status.next_redraw_target = RedrawTarget::Scheduled(
125
                        Instant::now()
126
                            .checked_add(Duration::from_secs_f32(1. / f32::from(fps)))
127
                            .unwrap(),
128
                    );
129
                }
130
                None => {
131
                    self.redraw_status.next_redraw_target = RedrawTarget::Never;
132
                }
133
            }
134
        }
135
    }
136

            
137
    pub(crate) const fn next_redraw_target(&self) -> RedrawTarget {
138
        self.redraw_status.next_redraw_target
139
    }
140

            
141
    pub(crate) fn can_wait_for_events(&self) -> bool {
142
        !self.should_redraw_now() && !self.redraw_status.needs_update
143
    }
144

            
145
    pub(crate) fn should_redraw_now(&self) -> bool {
146
        self.redraw_status.needs_render
147
            || match self.redraw_status.next_redraw_target {
148
                RedrawTarget::Never | RedrawTarget::None => false,
149
                RedrawTarget::Scheduled(scheduled_for) => scheduled_for < Instant::now(),
150
            }
151
    }
152

            
153
    pub(crate) fn request_close(&mut self) -> crate::Result<CloseResponse> {
154
        self.window.close_requested(WindowHandle(self.window_id))
155
    }
156

            
157
    pub(crate) fn process_input(&mut self, input: InputEvent) -> crate::Result<()> {
158
        self.window.process_input(
159
            input,
160
            &mut self.redraw_status,
161
            &Target::from(self.scene.clone()),
162
            WindowHandle(self.window_id),
163
        )
164
    }
165

            
166
    pub(crate) fn receive_character(&mut self, character: char) -> crate::Result<()> {
167
        self.window.receive_character(
168
            character,
169
            &mut self.redraw_status,
170
            &Target::from(self.scene.clone()),
171
            WindowHandle(self.window_id),
172
        )
173
    }
174

            
175
    pub(crate) fn initialize(&mut self) -> crate::Result<()> {
176
        self.window.initialize(
177
            &Target::from(self.scene.clone()),
178
            self.redraw_status.redraw_requester(),
179
            WindowHandle(self.window_id),
180
        )?;
181

            
182
        Ok(())
183
    }
184

            
185
    pub(crate) fn render(&mut self) -> crate::Result<()> {
186
        // Clear the redraw target first, so that if something inside of render
187
        // (or another thread) requests a redraw it will still be honored.
188
        self.clear_redraw_target();
189

            
190
        self.window.render(
191
            &Target {
192
                scene: self.scene.clone(),
193
                clip: None,
194
                offset: None,
195
            },
196
            &mut self.redraw_status,
197
            WindowHandle(self.window_id),
198
        )?;
199

            
200
        Ok(())
201
    }
202

            
203
    pub(crate) fn update(&mut self, target_fps: Option<u16>) -> crate::Result<()> {
204
        self.initialize_redraw_target(target_fps);
205
        self.set_has_updated();
206

            
207
        self.window.update(
208
            &Target {
209
                scene: self.scene.clone(),
210
                clip: None,
211
                offset: None,
212
            },
213
            &mut self.redraw_status,
214
            WindowHandle(self.window_id),
215
        )
216
    }
217

            
218
    pub(crate) fn additional_scale(&self) -> Scale<f32, Scaled, Points> {
219
        self.window.additional_scale()
220
    }
221

            
222
    pub(crate) fn scene(&self) -> Target {
223
        Target {
224
            scene: self.scene.clone(),
225
            clip: None,
226
            offset: None,
227
        }
228
    }
229

            
230
    pub(crate) fn scene_mut(&mut self) -> &'_ mut Scene {
231
        Arc::get_mut(&mut self.scene)
232
            .expect("Unable to lock scene. Users should not store any references to `Target`")
233
    }
234
}
235

            
236
#[derive(Debug, Clone, Copy)]
237
pub enum RedrawTarget {
238
    None,
239
    Never,
240
    Scheduled(Instant),
241
}
242

            
243
impl Default for RedrawTarget {
244
    fn default() -> Self {
245
        Self::None
246
    }
247
}
248

            
249
pub enum UpdateSchedule {
250
    Now,
251
    Scheduled(Instant),
252
}
253

            
254
impl RedrawTarget {
255
    pub(crate) const fn next_update_instant(&self) -> Option<UpdateSchedule> {
256
        match self {
257
            Self::Never => None,
258
            Self::None => Some(UpdateSchedule::Now),
259
            Self::Scheduled(scheduled_for) => Some(UpdateSchedule::Scheduled(*scheduled_for)),
260
        }
261
    }
262
}
263

            
264
impl UpdateSchedule {
265
    pub fn timeout_target(&self) -> Option<Instant> {
266
        match self {
267
            UpdateSchedule::Now => None,
268
            UpdateSchedule::Scheduled(scheduled_for) => {
269
                if &Instant::now() > scheduled_for {
270
                    None
271
                } else {
272
                    Some(*scheduled_for)
273
                }
274
            }
275
        }
276
    }
277
}