#![deny(missing_docs)]
#![deny(missing_copy_implementations)]
#![allow(unstable)]
extern crate clock_ticks;
#[macro_use]
extern crate quack;
use std::old_io::timer::sleep;
use std::time::duration::Duration;
use quack::{ ActOn, Action, GetFrom, Get, Pair };
use std::cmp;
#[derive(Copy)]
pub struct ShouldClose(pub bool);
impl Sized for ShouldClose {}
#[derive(Copy)]
pub struct Size(pub [u32; 2]);
impl Sized for Size {}
#[derive(Copy)]
pub struct SwapBuffers;
impl Sized for SwapBuffers {}
#[derive(Copy)]
pub struct PollEvent;
impl Sized for PollEvent {}
#[derive(Copy, Clone, PartialEq, Show)]
pub struct RenderArgs {
pub ext_dt: f64,
pub width: u32,
pub height: u32,
}
#[derive(Copy, Clone, PartialEq, Show)]
pub struct UpdateArgs {
pub dt: f64,
}
#[derive(Copy, Clone, PartialEq, Show)]
pub struct IdleArgs {
pub dt: f64
}
pub trait EventMap<I> {
fn render(args: RenderArgs) -> Self;
fn update(args: UpdateArgs) -> Self;
fn input(args: I) -> Self;
fn idle(IdleArgs) -> Self;
}
#[derive(Copy, Show, PartialEq, Eq)]
enum Idle {
No,
Yes
}
#[derive(Copy, Show)]
enum State {
Render,
SwapBuffers,
UpdateLoop(Idle),
HandleEvents,
Update,
}
#[derive(Copy)]
pub struct Ups(pub u64);
quack_set! {
events: Events[W, I, E]
fn (ups: Ups) {
let frames = ups.0;
events.dt_update_in_ns = BILLION / frames;
events.dt = 1.0 / frames as f64;
}
}
#[derive(Copy)]
pub struct MaxFps(pub u64);
quack_set! {
this: Events[W, I, E]
fn (max_fps: MaxFps) {
this.dt_frame_in_ns = BILLION / max_fps.0;
}
}
pub struct Events<W, I, E> {
window: W,
state: State,
last_update: u64,
last_frame: u64,
dt_update_in_ns: u64,
dt_frame_in_ns: u64,
dt: f64,
}
static BILLION: u64 = 1_000_000_000;
pub const DEFAULT_UPS: Ups = Ups(120);
pub const DEFAULT_MAX_FPS: MaxFps = MaxFps(60);
impl<W, I, E> Events<W, I, E> {
pub fn new(window: W) -> Events<W, I, E> {
let start = clock_ticks::precise_time_ns();
let Ups(updates_per_second) = DEFAULT_UPS;
let MaxFps(max_frames_per_second) = DEFAULT_MAX_FPS;
Events {
window: window,
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,
}
}
}
impl<W, I, E>
Iterator
for Events<W, I, E>
where
(ShouldClose, W): Pair<Data = ShouldClose, Object = W> + GetFrom,
(Size, W): Pair<Data = Size, Object = W> + GetFrom,
(SwapBuffers, W): Pair<Data = SwapBuffers, Object = W> + ActOn<()>,
(PollEvent, W): Pair<Data = PollEvent, Object = W> + ActOn<Option<I>>,
E: EventMap<I>,
{
type Item = E;
fn next(&mut self) -> Option<E> {
loop {
self.state = match self.state {
State::Render => {
let ShouldClose(should_close) = self.window.get();
if should_close { return None; }
let start_render = clock_ticks::precise_time_ns();
self.last_frame = start_render;
let Size([w, h]) = self.window.get();
if w != 0 && h != 0 {
self.state = State::SwapBuffers;
return Some(EventMap::render(RenderArgs {
ext_dt: (start_render - self.last_update) as f64
/ BILLION as f64,
width: w,
height: h,
}));
}
State::UpdateLoop(Idle::No)
}
State::SwapBuffers => {
self.window.action(SwapBuffers);
State::UpdateLoop(Idle::No)
}
State::UpdateLoop(ref mut idle) => {
let current_time = clock_ticks::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) = self.window.action(PollEvent) {
*idle = Idle::No;
return Some(EventMap::input(x));
} else if *idle == Idle::No {
*idle = Idle::Yes;
let seconds = ((next_event - current_time) as f64) / (BILLION as f64);
return Some(EventMap::idle(IdleArgs { dt: seconds }))
}
sleep( Duration::nanoseconds((next_event - current_time) as i64) );
State::UpdateLoop(Idle::No)
} else if next_event == next_frame {
State::Render
} else {
State::HandleEvents
}
}
State::HandleEvents => {
match self.window.action(PollEvent) {
None => State::Update,
Some(x) => { return Some(EventMap::input(x)); },
}
}
State::Update => {
self.state = State::UpdateLoop(Idle::No);
self.last_update += self.dt_update_in_ns;
return Some(EventMap::update(UpdateArgs{ dt: self.dt }));
}
};
}
}
}