main_loop 0.3.3

main loop for real time applications
use std::ptr;
use std::cell::RefCell;
use std::os::raw::{c_int, c_void};

use super::super::{now, ControlFlow};

extern "C" {
    fn emscripten_set_main_loop(
        func: unsafe extern "C" fn(),
        fps: c_int,
        simulate_infinite_loop: c_int,
    );
    fn emscripten_resume_main_loop();
    fn emscripten_pause_main_loop();
}

thread_local!(
    static MAIN_LOOP_CALLBACK: RefCell<*mut c_void> = RefCell::new(ptr::null_mut());
    static MAIN_MS: RefCell<f64> = RefCell::new(0.0);
);

unsafe extern "C" fn callback_wrapper<F>()
where
    F: FnMut(f64),
{
    MAIN_LOOP_CALLBACK.with(|ref_cell| {
        let closure = *ref_cell.borrow_mut() as *mut F;
        let ms = now() - MAIN_MS.with(|ref_cell| *ref_cell.borrow());
        (*closure)(ms);
    });
}

#[inline]
fn set_callback_loop<F>(callback: F)
where
    F: FnMut(f64),
{
    MAIN_LOOP_CALLBACK.with(|ref_cell| {
        *ref_cell.borrow_mut() = &callback as *const _ as *mut c_void;
    });

    unsafe {
        emscripten_set_main_loop(callback_wrapper::<F>, 0, 1);
        emscripten_pause_main_loop();
    }
}

#[inline(always)]
fn pause_loop() {
    unsafe {
        emscripten_pause_main_loop();
    }
}

#[inline(always)]
pub fn set_target_fps(_: f64) {}

#[inline(always)]
pub fn target_fps() -> f64 {
    1000.0 / 60.0
}

#[inline]
pub fn run<F>(mut callback: F)
where
    F: FnMut(f64) -> ControlFlow,
{
    set_callback_loop(move |ms| match callback(ms) {
        ControlFlow::Break => pause_loop(),
        ControlFlow::Continue => (),
    });
    MAIN_MS.with(|ref_cell| {
        *ref_cell.borrow_mut() = now();
    });
    unsafe {
        emscripten_resume_main_loop();
    }
}