A single-header main loop abstraction for native and Emscripten targets.
entry_point provides two overloads of execute_main_loop — one for raw frame callbacks, and one that manages the full lifecycle of a tuple of plugin modules.
entry_point::execute_main_loop([](float ft, auto cancel) {
// called every frame
// ft: time since last frame in seconds
// cancel(): stops the loop
}, fps);fps is optional and defaults to 60 on native. On Emscripten it is passed directly to emscripten_set_main_loop_arg and can be set to 0 to use the browser's requestAnimationFrame rate.
On native the loop runs on the calling thread and blocks until cancel() is called. On Emscripten the loop is handed off to the browser and the call returns immediately.
entry_point::execute_main_loop(state, fps, std::make_tuple(
MyModule(),
AnotherModule()
));Manages initialization, the frame loop, and teardown for a tuple of modules. Each module must implement:
const char* name();
void init(std::function<void(std::function<void()>)> done);
template<typename State, typename Cancel>
void run(State& state, float ft, Cancel cancel);- All modules are initialized concurrently by calling
initon each in tuple order. - Each module calls
donewhen ready, passing its own teardown callback. - Once all modules have called
done, the frame loop begins. - Each frame,
runis called on every module in tuple order. - Any module can stop the loop by calling
cancel()from withinrun. This triggers teardown of all modules in tuple order before the loop exits.
struct MyModule {
const char* name() { return "MyModule"; }
void init(std::function<void(std::function<void()>)> done) {
// setup
done([]() {
// teardown
});
}
template<typename State, typename Cancel>
void run(State& state, float ft, Cancel cancel) {
// called every frame
}
};- Designed for single-threaded use. Modules may manage their own threads internally if needed.
cancel()is idempotent — calling it more than once from different modules in the same frame is safe.- On Emscripten,
emscripten_set_main_loop_argis called once and cannot be called again for the lifetime of the page. Callingexecute_main_loopmore than once on Emscripten is undefined behavior. - In debug builds (
NDEBUGnot defined), an assertion fires if modules take more than 30 frames to complete initialization, catching modules that never call theirdonecallback.