#![deny(missing_docs)]
#![deny(missing_copy_implementations)]
extern crate window;
extern crate input;
extern crate viewport;
extern crate time;
use std::thread::sleep;
use std::time::Duration;
use std::cmp;
use window::Window;
use input::{ AfterRenderArgs, Event, IdleArgs, RenderArgs, UpdateArgs };
pub trait Events {
fn events(&self) -> WindowEvents;
}
impl<W> Events for W where W: Window {
fn events(&self) -> WindowEvents {
WindowEvents::new()
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum Idle {
No,
Yes
}
#[derive(Copy, Clone, Debug)]
enum State {
Render,
SwapBuffers,
UpdateLoop(Idle),
HandleEvents,
Update,
}
pub trait EventLoop: Sized {
fn set_ups(&mut self, frames: u64);
fn ups(mut self, frames: u64) -> Self {
self.set_ups(frames);
self
}
fn set_max_fps(&mut self, frames: u64);
fn max_fps(mut self, frames: u64) -> Self {
self.set_max_fps(frames);
self
}
fn set_swap_buffers(&mut self, enable: bool);
fn swap_buffers(mut self, enable: bool) -> Self {
self.set_swap_buffers(enable);
self
}
fn set_bench_mode(&mut self, enable: bool);
fn bench_mode(mut self, enable: bool) -> Self {
self.set_bench_mode(enable);
self
}
}
#[derive(Copy, Clone)]
pub struct WindowEvents {
state: State,
last_update: u64,
last_frame: u64,
dt_update_in_ns: u64,
dt_frame_in_ns: u64,
dt: f64,
swap_buffers: bool,
bench_mode: bool,
}
static BILLION: u64 = 1_000_000_000;
fn ns_to_duration(ns: u64) -> Duration {
let secs = ns / BILLION;
let nanos = (ns % BILLION) as u32;
Duration::new(secs, nanos)
}
pub const DEFAULT_UPS: u64 = 120;
pub const DEFAULT_MAX_FPS: u64 = 60;
impl WindowEvents
{
pub fn new() -> WindowEvents {
let start = time::precise_time_ns();
let updates_per_second = DEFAULT_UPS;
let max_frames_per_second = DEFAULT_MAX_FPS;
WindowEvents {
state: State::Render,
last_update: start,
last_frame: start,
dt_update_in_ns: BILLION / updates_per_second,
dt_frame_in_ns: BILLION / max_frames_per_second,
dt: 1.0 / updates_per_second as f64,
swap_buffers: true,
bench_mode: false,
}
}
pub fn next<W>(&mut self, window: &mut W) -> Option<Event<W::Event>>
where W: Window
{
loop {
self.state = match self.state {
State::Render => {
if window.should_close() { return None; }
if self.bench_mode {
self.last_frame += self.dt_frame_in_ns;
} else {
self.last_frame = time::precise_time_ns();
}
let size = window.size();
let draw_size = window.draw_size();
if size.width != 0 && size.height != 0 {
self.state = State::SwapBuffers;
return Some(Event::Render(RenderArgs {
ext_dt: (self.last_frame - self.last_update) as f64
/ BILLION as f64,
width: size.width,
height: size.height,
draw_width: draw_size.width,
draw_height: draw_size.height,
}));
}
State::UpdateLoop(Idle::No)
}
State::SwapBuffers => {
if self.swap_buffers {
window.swap_buffers();
self.state = State::UpdateLoop(Idle::No);
return Some(Event::AfterRender(AfterRenderArgs));
} else {
State::UpdateLoop(Idle::No)
}
}
State::UpdateLoop(ref mut idle) => {
if self.bench_mode {
let next_frame = self.last_frame + self.dt_frame_in_ns;
let next_update = self.last_update + self.dt_update_in_ns;
let next_event = cmp::min(next_frame, next_update);
if next_event == next_frame {
State::Render
} else {
State::HandleEvents
}
} else {
let current_time = time::precise_time_ns();
let next_frame = self.last_frame + self.dt_frame_in_ns;
let next_update = self.last_update + self.dt_update_in_ns;
let next_event = cmp::min(next_frame, next_update);
if next_event > current_time {
if let Some(x) = window.poll_event() {
*idle = Idle::No;
return Some(Event::Input(x));
} else if *idle == Idle::No {
*idle = Idle::Yes;
let seconds = ((next_event - current_time) as f64) / (BILLION as f64);
return Some(Event::Idle(IdleArgs { dt: seconds }))
}
sleep(ns_to_duration(next_event - current_time));
State::UpdateLoop(Idle::No)
} else if next_event == next_frame {
State::Render
} else {
State::HandleEvents
}
}
}
State::HandleEvents => {
if self.bench_mode {
match window.poll_event() {
None => State::Update,
Some(_) => State::HandleEvents,
}
} else {
match window.poll_event() {
None => State::Update,
Some(x) => { return Some(Event::Input(x)); },
}
}
}
State::Update => {
self.state = State::UpdateLoop(Idle::No);
self.last_update += self.dt_update_in_ns;
return Some(Event::Update(UpdateArgs{ dt: self.dt }));
}
};
}
}
}
impl EventLoop for WindowEvents {
fn set_ups(&mut self, frames: u64) {
self.dt_update_in_ns = BILLION / frames;
self.dt = 1.0 / frames as f64;
}
fn set_max_fps(&mut self, frames: u64) {
self.dt_frame_in_ns = BILLION / frames;
}
fn set_swap_buffers(&mut self, enable: bool) {
self.swap_buffers = enable;
}
fn set_bench_mode(&mut self, enable: bool) {
self.bench_mode = enable;
}
}