/*!@file \brief engine entry \details initializes necessary subsystems before invoking the preloader, which loads initial game data, before finally invoking the main loop main_loop(void), which may be run in either blocking or non-blocking mode. \author Mihrtec \date 2016 ------------------------------------------------------------------------------*/ #ifdef __EMSCRIPTEN__ /* Web Environment */ #define main em_main #include #include #else /* Traditional Environment */ #ifdef __Win32 #include #endif //__Win32 #include #endif /* ENVIRONMENT-AGNOSTIC DEFINES */ #include #include #include #include #include #define TRIGGERS quit_trigger #include #include /* exposed functions */ void main_loop(void); /* private functions */ static int main_init(void); /* unexposed externs */ extern int state_init(void); extern void state_tick(uint32_t delta_ticks); extern const char* state_get_error(void); extern void state_quit(void); extern void state_handle_event(SDL_Event *event); #if 0 extern int io_init(void); extern const char* io_get_error(void); extern void io_quit(void); #endif //TODO /* main jump buffer */ jmp_buf jmp_main; /*@ initializes subsystems and calls main_loop(void) main sets a jump buffer for its primary switch, which may be jumped to at any time. Jumping with a 0 return value is equivalent to calling setjmp directly, which initializes the system and begins the main loop. Jumping with any other value will process one of the directives associated with the exit codes in core.h */ #ifdef __EMSCRIPTEN__ #define main_loop() \ do { \ emscripten_set_main_loop(main_loop,0,0); \ TRIGGER_SET(quit_trigger, emscripten_cancel_main_loop); \ return 0; \ } while (0) #endif int main (int argc, char** argv) { switch(setjmp(jmp_main)) { case 0: if (main_init()) return -1; main_loop(); case EXIT_GRACEFUL: break; default: case EXIT_PANIC: //dump some debug info break; } TRIGGER(quit_trigger); return 0; } #ifdef __EMSCRIPTEN__ #undef main_loop #endif /*@ subsystem initializer Calling main_init() boots the system, and may be called multiple times to cause a system-wide reboot. @return 0 if successful, -1 SDL, -2 IMG, -3 TTF, -4 STATE. SDL and logging is available after this is called */ #define INIT(_cond,_errorstring,_quit) \ do { \ if (_cond) \ { fprintf(stderr, #_cond " failed: %s\n", _errorstring()); \ return -1; \ } \ TRIGGER_SET(quit_trigger, _quit); \ } while (0) #define SDL_FLAGS SDL_INIT_EVERYTHING & ~(SDL_INIT_TIMER | SDL_INIT_HAPTIC) static int main_init() { static uint32_t bInitialized = 0; if (bInitialized++) { TRIGGER(quit_trigger); SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Resetting [%d]\n", bInitialized); } INIT(SDL_Init(SDL_FLAGS) < 0, SDL_GetError, SDL_Quit); INIT(IMG_Init(IMG_INIT_PNG) != IMG_INIT_PNG, IMG_GetError, IMG_Quit); INIT(TTF_Init() == -1, TTF_GetError, TTF_Quit); INIT(state_init(), state_get_error, state_quit); INIT(fs_init(), fs_get_error, fs_quit); SDL_Log("Initialization Complete."); return 0; } /*@ The main loop may be compiled in blocking or non-blocking mode, and synchronizes time in ticks (milliseconds) as established by SDL anchored on the state_tick() event. */ void main_loop() { static uint32_t state_last_ticks = 0; SDL_Event event; uint32_t delta_ticks; #ifndef NON_BLOCKING_LOOPS loop: #endif /* user/system inputs */ while (SDL_PollEvent(&event)) state_handle_event(&event); /* change in time since last loop */ delta_ticks = SDL_GetTicks() - state_last_ticks; /* handle breakpoints (long pause likely a breakpoint) */ if (delta_ticks > 1000) { SDL_LogWarn(SDL_LOG_CATEGORY_SYSTEM, "Recovering from long pause."); delta_ticks = TARGET_DT; } /* tick the state manager and immediately save the time */ state_tick(delta_ticks); state_last_ticks = SDL_GetTicks(); //TODO: render(screen) //TODO: SDL_Flip(screen)::sdl_geterror #ifndef NON_BLOCKING_LOOPS #define DIV_UP(x,y) (((x) + ((y) / 2)) / (x)) /* yield if we have a substantial portion of time leftover */ delta_ticks += SDL_GetTicks() - state_last_ticks; if (delta_ticks < (TARGET_DT - DIV_UP(TARGET_DT, 10))) SDL_Delay(TARGET_DT - delta_ticks); goto loop; #endif }