event_loop/
lib.rs

1//! A Piston event loop for games and interactive applications
2
3#![deny(
4    rust_2018_compatibility,
5    rust_2018_idioms,
6    future_incompatible,
7    nonstandard_style,
8    unused,
9    clippy::all,
10    clippy::doc_markdown,
11    missing_docs,
12    missing_copy_implementations,
13    missing_debug_implementations
14)]
15
16use std::{
17    cmp,
18    time::{Duration, Instant},
19};
20
21use input::{AfterRenderArgs, Event, IdleArgs, RenderArgs, UpdateArgs};
22use window::Window;
23
24/// Tells whether last emitted event was idle or not.
25#[derive(Copy, Clone, Debug, PartialEq, Eq)]
26enum Idle {
27    No,
28    Yes,
29}
30
31#[derive(Copy, Clone, Debug)]
32enum State {
33    Render,
34    SwapBuffers,
35    UpdateLoop(Idle),
36    HandleEvents,
37    Update,
38}
39
40/// Stores event loop settings.
41#[derive(Copy, Clone, Debug)]
42pub struct EventSettings {
43    /// The maximum number of frames per second
44    ///
45    /// The frame rate can be lower because the
46    /// next frame is always scheduled from the previous frame.
47    /// This causes the frames to "slip" over time.
48    pub max_fps: u64,
49    /// The number of updates per second
50    ///
51    /// This is the fixed update rate on average over time.
52    /// If the event loop lags, it will try to catch up.
53    /// When set to `0`, update events are disabled.
54    pub ups: u64,
55    /// The number of delayed updates before skipping them to catch up.
56    /// When set to `0`, it will always try to catch up.
57    pub ups_reset: u64,
58    /// Enable or disable automatic swapping of buffers.
59    pub swap_buffers: bool,
60    /// Enable or disable benchmark mode.
61    /// When enabled, it will render and update without sleep and ignore input.
62    /// Used to test performance by playing through as fast as possible.
63    /// Requires `lazy` to be set to `false`.
64    pub bench_mode: bool,
65    /// Enable or disable rendering only when receiving input.
66    /// When enabled, update and idle events are disabled.
67    pub lazy: bool,
68}
69
70impl EventSettings {
71    /// Creates new with default settings.
72    pub fn new() -> EventSettings {
73        EventSettings {
74            max_fps: DEFAULT_MAX_FPS,
75            ups: DEFAULT_UPS,
76            swap_buffers: true,
77            bench_mode: false,
78            lazy: false,
79            ups_reset: DEFAULT_UPS_RESET,
80        }
81    }
82}
83
84impl Default for EventSettings {
85    fn default() -> EventSettings {
86        EventSettings::new()
87    }
88}
89
90/// An event loop iterator
91///
92/// *Warning: Because the iterator polls events from the window back-end,
93/// it must be used on the same thread as the window back-end (usually main thread),
94/// unless the window back-end supports multi-thread event polling.*
95#[derive(Copy, Clone, Debug)]
96pub struct Events {
97    state: State,
98    last_update: Instant,
99    last_frame: Instant,
100    dt_update_in_ns: u64,
101    dt_frame_in_ns: u64,
102    dt: f64,
103    settings: EventSettings,
104    first_frame: bool,
105}
106
107static BILLION: u64 = 1_000_000_000;
108
109fn ns_to_duration(ns: u64) -> Duration {
110    let secs = ns / BILLION;
111    let nanos = (ns % BILLION) as u32;
112    Duration::new(secs, nanos)
113}
114
115fn duration_to_secs(dur: Duration) -> f64 {
116    dur.as_secs() as f64 + dur.subsec_nanos() as f64 / 1_000_000_000.0
117}
118
119/// The default updates per second.
120pub const DEFAULT_UPS: u64 = 120;
121/// The default delayed updates reset.
122pub const DEFAULT_UPS_RESET: u64 = 2;
123/// The default maximum frames per second.
124pub const DEFAULT_MAX_FPS: u64 = 60;
125
126macro_rules! next_with_sleep {
127    ($self:ident, $window:ident, $sleep:ident $(. $x:tt)?) => {{
128        if $self.settings.lazy || $self.settings.ups == 0 {
129            // This mode does not emit update events.
130            // More commonly used in UI applications.
131            if $window.should_close() {
132                return None;
133            }
134            match $self.state {
135                State::SwapBuffers => {
136                    if $self.settings.swap_buffers {
137                        $window.swap_buffers();
138                    }
139                    // This mode needs no `Render` state.
140                    $self.state = State::UpdateLoop(Idle::No);
141                    return Some(AfterRenderArgs.into());
142                }
143                State::HandleEvents => {
144                    if !$self.settings.bench_mode {
145                        // Poll input events until event queue is empty.
146                        if let Some(ev) = $window.poll_event() {
147                            return Some(ev);
148                        }
149                    }
150                    $self.state = State::Render;
151                }
152                _ => {}
153            }
154            loop {
155                // Handle input events before rendering,
156                // because window might be closed and destroy
157                // the graphics context.
158                if let Some(e) = $window.poll_event() {
159                    if $self.settings.bench_mode {
160                        // Ignore input events in benchmark mode.
161                        // This is to avoid the input events affecting
162                        // the application state when benchmarking.
163                        continue;
164                    } else {
165                        return Some(e);
166                    }
167                }
168                if $window.should_close() {
169                    return None;
170                }
171
172                if !$self.settings.bench_mode {
173                    if $self.settings.lazy {
174                        // A lazy event loop always waits until next event, ignoring time to render.
175                        if let State::UpdateLoop(_) = $self.state {
176                            // Wait for next input event.
177                            let ev = $window.wait_event();
178                            // Handle rest of events before rendering.
179                            $self.state = State::HandleEvents;
180                            return Some(ev);
181                        }
182                    } else {
183                        let current_time = Instant::now();
184                        let next_frame = $self.last_frame + ns_to_duration($self.dt_frame_in_ns);
185                        if !$self.first_frame && next_frame > current_time {
186                            if let State::UpdateLoop(Idle::No) = $self.state {
187                                // Emit idle event with time until next frame,
188                                // in case the application wants to do some background work.
189                                $self.state = State::UpdateLoop(Idle::Yes);
190                                let seconds = duration_to_secs(next_frame - current_time);
191                                return Some(IdleArgs { dt: seconds }.into());
192                            }
193                            match $window.wait_event_timeout(next_frame - current_time) {
194                                None => {}
195                                Some(x) => {
196                                    // Handle rest of events before rendering.
197                                    $self.state = State::HandleEvents;
198                                    return Some(x);
199                                }
200                            }
201                        }
202                    }
203                }
204
205                $self.first_frame = false;
206
207                // In normal mode, let the FPS slip if late.
208                $self.last_frame = Instant::now();
209
210                let size = $window.size();
211                let draw_size = $window.draw_size();
212                if size.width != 0.0 && size.height != 0.0 {
213                    // Swap buffers next time.
214                    $self.state = State::SwapBuffers;
215                    return Some(
216                        RenderArgs {
217                            ext_dt: 0.0,
218                            window_size: size.into(),
219                            draw_size: draw_size.into(),
220                        }
221                        .into(),
222                    );
223                } else {
224                    // Can not render at this time.
225                    $self.state = State::UpdateLoop(Idle::No);
226                }
227            }
228        }
229
230        loop {
231            if $window.should_close() {
232                return None;
233            }
234            $self.state = match $self.state {
235                State::Render => {
236                    // Handle input events before rendering,
237                    // because window might be closed and destroy
238                    // the graphics context.
239                    if let Some(e) = $window.poll_event() {
240                        if $self.settings.bench_mode {
241                            // Ignore input events in benchmark mode.
242                            // This is to avoid the input events affecting
243                            // the application state when benchmarking.
244                            continue;
245                        } else {
246                            return Some(e);
247                        }
248                    }
249                    if $window.should_close() {
250                        return None;
251                    }
252
253                    if $self.settings.bench_mode {
254                        // In benchmark mode, pretend FPS is perfect.
255                        $self.last_frame += ns_to_duration($self.dt_frame_in_ns);
256                    } else {
257                        // In normal mode, let the FPS slip if late.
258                        $self.last_frame = Instant::now();
259                    }
260
261                    let size = $window.size();
262                    let draw_size = $window.draw_size();
263                    if size.width != 0.0 && size.height != 0.0 {
264                        // Swap buffers next time.
265                        $self.state = State::SwapBuffers;
266                        return Some(
267                            RenderArgs {
268                                // Extrapolate time forward to allow smooth motion.
269                                ext_dt: duration_to_secs(
270                                    $self.last_frame.duration_since($self.last_update),
271                                ),
272                                window_size: size.into(),
273                                draw_size: draw_size.into(),
274                            }
275                            .into(),
276                        );
277                    }
278
279                    State::UpdateLoop(Idle::No)
280                }
281                State::SwapBuffers => {
282                    if $self.settings.swap_buffers {
283                        $window.swap_buffers();
284                    }
285                    $self.state = State::UpdateLoop(Idle::No);
286                    return Some(AfterRenderArgs.into());
287                }
288                State::UpdateLoop(ref mut idle) => {
289                    if $self.settings.bench_mode {
290                        // In benchmark mode, pick the next event without sleep.
291                        // Idle and input events are ignored.
292                        // This is to avoid the input events affecting
293                        // the application state when benchmarking.
294                        let next_frame = $self.last_frame + ns_to_duration($self.dt_frame_in_ns);
295                        let next_update = $self.last_update + ns_to_duration($self.dt_update_in_ns);
296                        let next_event = cmp::min(next_frame, next_update);
297                        if next_event == next_frame {
298                            State::Render
299                        } else {
300                            State::HandleEvents
301                        }
302                    } else {
303                        let current_time = Instant::now();
304                        let next_frame = $self.last_frame + ns_to_duration($self.dt_frame_in_ns);
305                        let next_update = $self.last_update + ns_to_duration($self.dt_update_in_ns);
306                        let next_event = cmp::min(next_frame, next_update);
307                        if next_event > current_time {
308                            if let Some(x) = $window.poll_event() {
309                                *idle = Idle::No;
310                                return Some(x);
311                            } else if *idle == Idle::No {
312                                *idle = Idle::Yes;
313                                let seconds = duration_to_secs(next_event - current_time);
314                                return Some(IdleArgs { dt: seconds }.into());
315                            }
316                            $sleep(next_event - current_time)$(.$x)?;
317                            State::UpdateLoop(Idle::No)
318                        } else if next_event == next_frame {
319                            State::Render
320                        } else {
321                            State::HandleEvents
322                        }
323                    }
324                }
325                State::HandleEvents => {
326                    if $self.settings.bench_mode {
327                        // Ignore input events.
328                        // This is to avoid the input events affecting
329                        // the application state when benchmarking.
330                        match $window.poll_event() {
331                            None => State::Update,
332                            Some(_) => State::HandleEvents,
333                        }
334                    } else {
335                        // Handle all events before updating.
336                        match $window.poll_event() {
337                            None => State::Update,
338                            x => return x,
339                        }
340                    }
341                }
342                State::Update => {
343                    $self.state = State::UpdateLoop(Idle::No);
344                    if !$self.settings.bench_mode
345                        && $self.settings.ups_reset > 0
346                        && Instant::now() - $self.last_update
347                            > ns_to_duration($self.settings.ups_reset * $self.dt_update_in_ns)
348                    {
349                        // Skip updates because CPU is too busy.
350                        $self.last_update = Instant::now();
351                    } else {
352                        // Use the update state stored right after sleep.
353                        $self.last_update += ns_to_duration($self.dt_update_in_ns);
354                    }
355                    return Some(UpdateArgs { dt: $self.dt }.into());
356                }
357            };
358        }
359    }};
360}
361
362impl Events {
363    /// Creates a new event iterator with default UPS and FPS settings.
364    pub fn new(settings: EventSettings) -> Events {
365        let start = Instant::now();
366        Events {
367            state: State::Render,
368            last_update: start,
369            last_frame: start,
370            dt_update_in_ns: if settings.ups == 0 {
371                0
372            } else {
373                BILLION / settings.ups
374            },
375            dt_frame_in_ns: BILLION / settings.max_fps,
376            dt: if settings.ups == 0 {
377                0.0
378            } else {
379                1.0 / settings.ups as f64
380            },
381            settings,
382            first_frame: true,
383        }
384    }
385
386    /// Returns the next event.
387    pub fn next<W>(&mut self, window: &mut W) -> Option<Event>
388    where
389        W: Window,
390    {
391        let sleep = spin_sleep::sleep;
392        next_with_sleep!(self, window, sleep)
393    }
394
395    /// Returns the next event.
396    #[cfg(feature = "async")]
397    pub async fn async_next<W>(&mut self, window: &mut W) -> Option<Event>
398    where
399        W: Window,
400    {
401        let sleep = tokio::time::sleep;
402        next_with_sleep!(self, window, sleep .await)
403    }
404}
405
406/// Methods implemented for changing event loop settings.
407pub trait EventLoop: Sized {
408    /// Returns event loop settings.
409    fn get_event_settings(&self) -> EventSettings;
410    /// Sets event loop settings.
411    fn set_event_settings(&mut self, settings: EventSettings);
412
413    /// The number of updates per second
414    ///
415    /// This is the fixed update rate on average over time.
416    /// If the event loop lags, it will try to catch up.
417    /// When set to `0`, update events are disabled.
418    fn set_ups(&mut self, frames: u64) {
419        let old_settings = self.get_event_settings();
420        self.set_event_settings(EventSettings {
421            ups: frames,
422            ..old_settings
423        });
424    }
425
426    /// The number of updates per second
427    ///
428    /// This is the fixed update rate on average over time.
429    /// If the event loop lags, it will try to catch up.
430    /// When set to `0`, update events are disabled.
431    fn ups(mut self, frames: u64) -> Self {
432        self.set_ups(frames);
433        self
434    }
435
436    /// The number of delayed updates before skipping them to catch up.
437    /// When set to `0`, it will always try to catch up.
438    fn set_ups_reset(&mut self, frames: u64) {
439        let old_settings = self.get_event_settings();
440        self.set_event_settings(EventSettings {
441            ups_reset: frames,
442            ..old_settings
443        });
444    }
445
446    /// The number of delayed updates before skipping them to catch up.
447    /// When set to `0`, it will always try to catch up.
448    fn ups_reset(mut self, frames: u64) -> Self {
449        self.set_ups_reset(frames);
450        self
451    }
452
453    /// The maximum number of frames per second
454    ///
455    /// The frame rate can be lower because the
456    /// next frame is always scheduled from the previous frame.
457    /// This causes the frames to "slip" over time.
458    fn set_max_fps(&mut self, frames: u64) {
459        let old_settings = self.get_event_settings();
460        self.set_event_settings(EventSettings {
461            max_fps: frames,
462            ..old_settings
463        })
464    }
465
466    /// The maximum number of frames per second
467    ///
468    /// The frame rate can be lower because the
469    /// next frame is always scheduled from the previous frame.
470    /// This causes the frames to "slip" over time.
471    fn max_fps(mut self, frames: u64) -> Self {
472        self.set_max_fps(frames);
473        self
474    }
475
476    /// Enable or disable automatic swapping of buffers.
477    fn set_swap_buffers(&mut self, enable: bool) {
478        let old_settings = self.get_event_settings();
479        self.set_event_settings(EventSettings {
480            swap_buffers: enable,
481            ..old_settings
482        })
483    }
484
485    /// Enable or disable automatic swapping of buffers.
486    fn swap_buffers(mut self, enable: bool) -> Self {
487        self.set_swap_buffers(enable);
488        self
489    }
490
491    /// Enable or disable benchmark mode.
492    /// When enabled, it will render and update without sleep and ignore input.
493    /// Used to test performance by playing through as fast as possible.
494    /// Requires `lazy` to be set to `false`.
495    fn set_bench_mode(&mut self, enable: bool) {
496        let old_settings = self.get_event_settings();
497        self.set_event_settings(EventSettings {
498            bench_mode: enable,
499            ..old_settings
500        })
501    }
502
503    /// Enable or disable benchmark mode.
504    /// When enabled, it will render and update without sleep and ignore input.
505    /// Used to test performance by playing through as fast as possible.
506    /// Requires `lazy` to be set to `false`.
507    fn bench_mode(mut self, enable: bool) -> Self {
508        self.set_bench_mode(enable);
509        self
510    }
511
512    /// Enable or disable rendering only when receiving input.
513    /// When enabled, update events are disabled.
514    /// Idle events are emitted while receiving input.
515    fn set_lazy(&mut self, enable: bool) {
516        let old_settings = self.get_event_settings();
517        self.set_event_settings(EventSettings {
518            lazy: enable,
519            ..old_settings
520        })
521    }
522
523    /// Enable or disable rendering only when receiving input.
524    /// When enabled, update events are disabled.
525    /// Idle events are emitted while receiving input.
526    fn lazy(mut self, enable: bool) -> Self {
527        self.set_lazy(enable);
528        self
529    }
530}
531
532impl EventLoop for EventSettings {
533    fn get_event_settings(&self) -> Self {
534        *self
535    }
536    fn set_event_settings(&mut self, settings: Self) {
537        *self = settings;
538    }
539}
540
541impl EventLoop for Events {
542    fn get_event_settings(&self) -> EventSettings {
543        self.settings
544    }
545    fn set_event_settings(&mut self, settings: EventSettings) {
546        // Reset event loop to initial state.
547        *self = Events::new(settings);
548    }
549}