#![deny(missing_docs)]
#![deny(missing_copy_implementations)]
extern crate window;
extern crate input;
use std::thread::sleep;
use std::time::{Duration, Instant};
use std::cmp;
use window::Window;
use input::{Event, AfterRenderArgs, IdleArgs, RenderArgs, UpdateArgs};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum Idle {
No,
Yes,
}
#[derive(Copy, Clone, Debug)]
enum State {
Render,
SwapBuffers,
UpdateLoop(Idle),
HandleEvents,
Update,
}
#[derive(Copy, Clone, Debug)]
pub struct EventSettings {
pub max_fps: u64,
pub ups: u64,
pub ups_reset: u64,
pub swap_buffers: bool,
pub bench_mode: bool,
pub lazy: bool,
}
impl EventSettings {
pub fn new() -> EventSettings {
EventSettings {
max_fps: DEFAULT_MAX_FPS,
ups: DEFAULT_UPS,
swap_buffers: true,
bench_mode: false,
lazy: false,
ups_reset: DEFAULT_UPS_RESET,
}
}
}
impl Default for EventSettings {
fn default() -> EventSettings {
EventSettings::new()
}
}
#[derive(Copy, Clone)]
pub struct Events {
state: State,
last_update: Instant,
last_frame: Instant,
dt_update_in_ns: u64,
dt_frame_in_ns: u64,
dt: f64,
settings: EventSettings,
first_frame: 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)
}
fn duration_to_secs(dur: Duration) -> f64 {
dur.as_secs() as f64 + dur.subsec_nanos() as f64 / 1_000_000_000.0
}
pub const DEFAULT_UPS: u64 = 120;
pub const DEFAULT_UPS_RESET: u64 = 2;
pub const DEFAULT_MAX_FPS: u64 = 60;
impl Events {
pub fn new(settings: EventSettings) -> Events {
let start = Instant::now();
Events {
state: State::Render,
last_update: start,
last_frame: start,
dt_update_in_ns: if settings.ups == 0 {
0
} else {
BILLION / settings.ups
},
dt_frame_in_ns: BILLION / settings.max_fps,
dt: if settings.ups == 0 {
0.0
} else {
1.0 / settings.ups as f64
},
settings: settings,
first_frame: true,
}
}
pub fn next<W>(&mut self, window: &mut W) -> Option<Event>
where W: Window
{
if self.settings.lazy || self.settings.ups == 0 {
if window.should_close() {
return None;
}
match self.state {
State::SwapBuffers => {
if self.settings.swap_buffers {
window.swap_buffers();
}
self.state = State::UpdateLoop(Idle::No);
return Some(AfterRenderArgs.into());
}
State::HandleEvents => {
if !self.settings.bench_mode {
if let Some(ev) = window.poll_event() {
return Some(ev);
}
}
self.state = State::Render;
}
_ => {}
}
loop {
if let Some(e) = window.poll_event() {
if self.settings.bench_mode {
continue;
} else {
return Some(e);
}
}
if window.should_close() {
return None;
}
if !self.settings.bench_mode {
if self.settings.lazy {
if let State::UpdateLoop(_) = self.state {
let ev = window.wait_event();
self.state = State::HandleEvents;
return Some(ev);
}
} else {
let current_time = Instant::now();
let next_frame = self.last_frame + ns_to_duration(self.dt_frame_in_ns);
if !self.first_frame && next_frame > current_time {
if let State::UpdateLoop(Idle::No) = self.state {
self.state = State::UpdateLoop(Idle::Yes);
let seconds = duration_to_secs(next_frame - current_time);
return Some(IdleArgs { dt: seconds }.into());
}
match window.wait_event_timeout(next_frame - current_time) {
None => {}
Some(x) => {
self.state = State::HandleEvents;
return Some(x)
}
}
}
}
}
self.first_frame = false;
self.last_frame = Instant::now();
let size = window.size();
let draw_size = window.draw_size();
if size.width != 0.0 && size.height != 0.0 {
self.state = State::SwapBuffers;
return Some(RenderArgs {
ext_dt: 0.0,
window_size: size.into(),
draw_size: draw_size.into(),
}.into());
} else {
self.state = State::UpdateLoop(Idle::No);
}
}
}
loop {
if window.should_close() {
return None;
}
self.state = match self.state {
State::Render => {
if let Some(e) = window.poll_event() {
if self.settings.bench_mode {
continue;
} else {
return Some(e);
}
}
if window.should_close() {
return None;
}
if self.settings.bench_mode {
self.last_frame += ns_to_duration(self.dt_frame_in_ns);
} else {
self.last_frame = Instant::now();
}
let size = window.size();
let draw_size = window.draw_size();
if size.width != 0.0 && size.height != 0.0 {
self.state = State::SwapBuffers;
return Some(RenderArgs {
ext_dt: duration_to_secs(self.last_frame
.duration_since(self.last_update)),
window_size: size.into(),
draw_size: draw_size.into(),
}.into());
}
State::UpdateLoop(Idle::No)
}
State::SwapBuffers => {
if self.settings.swap_buffers {
window.swap_buffers();
}
self.state = State::UpdateLoop(Idle::No);
return Some(AfterRenderArgs.into());
}
State::UpdateLoop(ref mut idle) => {
if self.settings.bench_mode {
let next_frame = self.last_frame + ns_to_duration(self.dt_frame_in_ns);
let next_update = self.last_update + ns_to_duration(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 = Instant::now();
let next_frame = self.last_frame + ns_to_duration(self.dt_frame_in_ns);
let next_update = self.last_update + ns_to_duration(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(x);
} else if *idle == Idle::No {
*idle = Idle::Yes;
let seconds = duration_to_secs(next_event - current_time);
return Some(IdleArgs { dt: seconds }.into());
}
sleep(next_event - current_time);
State::UpdateLoop(Idle::No)
} else if next_event == next_frame {
State::Render
} else {
State::HandleEvents
}
}
}
State::HandleEvents => {
if self.settings.bench_mode {
match window.poll_event() {
None => State::Update,
Some(_) => State::HandleEvents,
}
} else {
match window.poll_event() {
None => State::Update,
x => return x,
}
}
}
State::Update => {
self.state = State::UpdateLoop(Idle::No);
if !self.settings.bench_mode && self.settings.ups_reset > 0 &&
Instant::now() - self.last_update >
ns_to_duration(self.settings.ups_reset * self.dt_update_in_ns) {
self.last_update = Instant::now();
} else {
self.last_update += ns_to_duration(self.dt_update_in_ns);
}
return Some(UpdateArgs { dt: self.dt }.into());
}
};
}
}
}
pub trait EventLoop: Sized {
fn get_event_settings(&self) -> EventSettings;
fn set_event_settings(&mut self, settings: EventSettings);
fn set_ups(&mut self, frames: u64) {
let old_settings = self.get_event_settings();
self.set_event_settings(EventSettings { ups: frames, ..old_settings });
}
fn ups(mut self, frames: u64) -> Self {
self.set_ups(frames);
self
}
fn set_ups_reset(&mut self, frames: u64) {
let old_settings = self.get_event_settings();
self.set_event_settings(EventSettings { ups_reset: frames, ..old_settings });
}
fn ups_reset(mut self, frames: u64) -> Self {
self.set_ups_reset(frames);
self
}
fn set_max_fps(&mut self, frames: u64) {
let old_settings = self.get_event_settings();
self.set_event_settings(EventSettings { max_fps: frames, ..old_settings })
}
fn max_fps(mut self, frames: u64) -> Self {
self.set_max_fps(frames);
self
}
fn set_swap_buffers(&mut self, enable: bool) {
let old_settings = self.get_event_settings();
self.set_event_settings(EventSettings { swap_buffers: enable, ..old_settings })
}
fn swap_buffers(mut self, enable: bool) -> Self {
self.set_swap_buffers(enable);
self
}
fn set_bench_mode(&mut self, enable: bool) {
let old_settings = self.get_event_settings();
self.set_event_settings(EventSettings { bench_mode: enable, ..old_settings })
}
fn bench_mode(mut self, enable: bool) -> Self {
self.set_bench_mode(enable);
self
}
fn set_lazy(&mut self, enable: bool) {
let old_settings = self.get_event_settings();
self.set_event_settings(EventSettings { lazy: enable, ..old_settings })
}
fn lazy(mut self, enable: bool) -> Self {
self.set_lazy(enable);
self
}
}
impl EventLoop for EventSettings {
fn get_event_settings(&self) -> Self {
*self
}
fn set_event_settings(&mut self, settings: Self) {
*self = settings;
}
}
impl EventLoop for Events {
fn get_event_settings(&self) -> EventSettings {
self.settings
}
fn set_event_settings(&mut self, settings: EventSettings) {
*self = Events::new(settings);
}
}