#![deny(missing_docs)]
#![deny(missing_copy_implementations)]
extern crate clock_ticks;
extern crate window;
extern crate viewport;
use std::thread::sleep_ms;
use std::cmp;
use std::marker::PhantomData;
use std::cell::RefCell;
use std::rc::Rc;
use window::Window;
use viewport::Viewport;
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct RenderArgs {
pub ext_dt: f64,
pub width: u32,
pub height: u32,
pub draw_width: u32,
pub draw_height: u32,
}
impl RenderArgs {
pub fn viewport(&self) -> Viewport {
Viewport {
rect: [0, 0, self.draw_width as i32, self.draw_height as i32],
window_size: [self.width, self.height],
draw_size: [self.draw_width, self.draw_height],
}
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct AfterRenderArgs;
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct UpdateArgs {
pub dt: f64,
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct IdleArgs {
pub dt: f64
}
pub trait EventMap<I> {
fn render(args: RenderArgs) -> Self;
fn after_render(args: AfterRenderArgs) -> Self;
fn update(args: UpdateArgs) -> Self;
fn input(args: I) -> Self;
fn idle(IdleArgs) -> Self;
}
#[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
}
}
pub struct WindowEvents<W, E>
where
W: Window,
E: EventMap<<W as Window>::Event>
{
window: Rc<RefCell<W>>,
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,
_marker_e: PhantomData<E>,
}
static BILLION: u64 = 1_000_000_000;
fn ns_to_ms(ns: u64) -> u32 {
(ns / 1_000_000) as u32
}
pub const DEFAULT_UPS: u64 = 120;
pub const DEFAULT_MAX_FPS: u64 = 60;
impl<W, E> WindowEvents<W, E>
where
W: Window,
E: EventMap<<W as Window>::Event>
{
pub fn new(window: Rc<RefCell<W>>) -> WindowEvents<W, E> {
let start = clock_ticks::precise_time_ns();
let updates_per_second = DEFAULT_UPS;
let max_frames_per_second = DEFAULT_MAX_FPS;
WindowEvents {
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,
swap_buffers: true,
bench_mode: false,
_marker_e: PhantomData,
}
}
}
impl<W, E> EventLoop for WindowEvents<W, E>
where
W: Window,
E: EventMap<<W as Window>::Event>
{
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;
}
}
impl<W, E> Iterator for WindowEvents<W, E>
where
W: Window,
E: EventMap<<W as Window>::Event>,
{
type Item = E;
fn next(&mut self) -> Option<E> {
loop {
self.state = match self.state {
State::Render => {
let window = self.window.borrow();
if window.should_close() { return None; }
if self.bench_mode {
self.last_frame += self.dt_frame_in_ns;
} else {
self.last_frame = clock_ticks::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(EventMap::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 {
self.window.borrow_mut().swap_buffers();
self.state = State::UpdateLoop(Idle::No);
return Some(EventMap::after_render(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 = 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.borrow_mut().poll_event() {
*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_ms(ns_to_ms(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 self.window.borrow_mut().poll_event() {
None => State::Update,
Some(_) => State::HandleEvents,
}
} else {
match self.window.borrow_mut().poll_event() {
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 }));
}
};
}
}
}