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}