1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
extern crate sdl2;

#[macro_use] pub mod types;
pub mod object;
pub mod pixel;
pub mod renderer;
pub mod screen;

use sdl2::event::Event as SdlEvent;

use std::error;
use std::thread;
use std::time::Duration;
use std::time::Instant;

mod texture;
mod utils;

use renderer::Renderer;
use screen::GraphicalScreen;


const NANOS_PER_SECOND: u32 = 1_000_000_000;

type InitFunc<WorldState, S> =
    fn (&mut Renderer<S>) -> Result<WorldState, Box<error::Error>>;

type ParseEventFunc<WorldState> =
    fn (&mut LoopState, &mut WorldState, SdlEvent);
type UpdateFunc<WorldState> = fn (&mut WorldState) -> bool;
type RenderFunc<WorldState, S> =
    fn (&mut Renderer<S>, &WorldState) -> Result<(), Box<error::Error>>;

pub fn main_loop<'a, WorldState>
(
    screen_config: ScreenConfig,
    init: InitFunc<WorldState, screen::GraphicalScreen<'a>>,

    parse_event: ParseEventFunc<WorldState>,
    update:      UpdateFunc<WorldState>,
    render:      RenderFunc<WorldState, screen::GraphicalScreen<'a>>,
)
    -> Result<(), Box<error::Error>>
{
    // Initialize screen.
    let sdl_context = sdl2::init().unwrap();
    let screen = try!(GraphicalScreen::new(
        screen_config.title,
        screen_config.width,
        screen_config.height,
        &sdl_context,
    ));
    let mut renderer = Renderer::new(screen);
    let mut event_pump = sdl_context.event_pump().unwrap();

    let mut world_state = try!(init(&mut renderer));

    // Main loop.
    let frame_len_nanos = NANOS_PER_SECOND / screen_config.target_fps;
    let mut loop_state = LoopState::new();
    while loop_state.running {
        // Time frame length.
        let frame_start = Instant::now();

        // Update and render frame.
        for event in event_pump.poll_iter() {
            parse_event(&mut loop_state, &mut world_state, event);
        }
        if loop_state.should_tick() {
            loop_state.step = false;
            let frame_dirty = update(&mut world_state);
            if  frame_dirty { try!(render(&mut renderer, &world_state)); }
        }

        // Sleep until end of frame.
        let frame_len = Instant::now() - frame_start;
        let target_frame_len = Duration::new(0, frame_len_nanos);
        if frame_len < target_frame_len {
            thread::sleep(target_frame_len - frame_len);
        } else {
            println!("slowed down!");
        }
    }

    Ok(())
}

pub struct ScreenConfig {
    pub title:  &'static str,
    pub width:  u32,
    pub height: u32,
    pub target_fps: u32
}

pub struct LoopState {
    pub running: bool,
    pub paused: bool,
    pub step: bool,
}

impl LoopState {
    pub fn new() -> LoopState {
        LoopState {
            running: true,
            paused: false,
            step:   false,
        }
    }

    pub fn should_tick(&self) -> bool {
        !self.paused || self.step
    }
}