lime_main_loop/
lib.rs

1extern crate failure;
2#[macro_use]
3extern crate log;
4extern crate winit;
5
6mod action;
7mod ticker;
8
9use std::collections::BinaryHeap;
10use std::sync::mpsc;
11use std::time::{Duration, Instant};
12use std::{cmp, thread};
13
14use winit::{ControlFlow, DeviceEvent, Event, EventsLoop, EventsLoopProxy, WindowEvent};
15
16use self::action::Action;
17use self::ticker::Ticker;
18
19pub trait App: Sized {
20    const UPDATES_PER_SECOND: u32;
21    const RENDERS_PER_SECOND: u32;
22
23    fn update(&mut self, dt: Duration);
24    fn render(&mut self, dt: Duration);
25    fn window_event(&mut self, event: WindowEvent) -> ControlFlow;
26    fn device_event(&mut self, event: DeviceEvent) -> ControlFlow;
27}
28
29pub enum AppState {
30    Continue,
31    Quit,
32}
33
34pub fn run<A, F>(build: F)
35where
36    A: App,
37    F: FnOnce(&EventsLoop) -> A,
38{
39    let mut events_loop = EventsLoop::new();
40    let mut app = build(&events_loop);
41
42    const SECOND: Duration = Duration::from_secs(1);
43    let intervals: [Duration; Action::COUNT] = [
44        SECOND / A::UPDATES_PER_SECOND,
45        SECOND / A::RENDERS_PER_SECOND,
46        SECOND,
47    ];
48
49    let (tx, rx) = mpsc::channel();
50    let proxy = events_loop.create_proxy();
51    thread::spawn(move || wakeup(proxy, tx, &intervals));
52
53    let mut update_ticker = Ticker::new();
54    let mut render_ticker = Ticker::new();
55    events_loop.run_forever(|event| match event {
56        Event::WindowEvent { event, .. } => app.window_event(event),
57        Event::DeviceEvent { event, .. } => app.device_event(event),
58        Event::Awakened => {
59            let (deadline, action) = rx.recv().unwrap();
60            if deadline < Instant::now() {
61                trace!("Skipping action {:?}.", action);
62            } else {
63                match action {
64                    Action::Update => app.update(update_ticker.tick()),
65                    Action::Render => app.render(render_ticker.tick()),
66                    Action::Log => {
67                        info!("Updates per second: {}.", update_ticker.split());
68                        info!("Renders per second: {}.", render_ticker.split());
69                    }
70                }
71            }
72            ControlFlow::Continue
73        }
74        Event::Suspended(_) => ControlFlow::Continue,
75    })
76}
77
78fn wakeup(
79    proxy: EventsLoopProxy,
80    tx: mpsc::Sender<(Instant, Action)>,
81    intervals: &[Duration; Action::COUNT],
82) {
83    if intervals.len() > 0 {
84        let mut heap: BinaryHeap<_> = Action::values()
85            .map(|action| (Instant::now(), action))
86            .map(cmp::Reverse)
87            .collect();
88
89        loop {
90            let cmp::Reverse((time, action)) = heap.pop().unwrap();
91            let next = time + intervals[action as usize];
92            heap.push(cmp::Reverse((next, action)));
93
94            let now = Instant::now();
95            if now < time {
96                thread::sleep(time - now);
97            }
98
99            if let Err(_) = tx.send((next, action)) {
100                return;
101            }
102            if let Err(_) = proxy.wakeup() {
103                return;
104            }
105        }
106    }
107}