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}