build system 3
[henge/webcc.git] / src / bin / dist / the_march.c
diff --git a/src/bin/dist/the_march.c b/src/bin/dist/the_march.c
new file mode 100644 (file)
index 0000000..db2ff34
--- /dev/null
@@ -0,0 +1,171 @@
+/*!@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 <SDL_ttf.h>
+#include <emscripten/emscripten.h>
+#else
+/* Traditional Environment */
+#ifdef __Win32
+#include <windows.h>
+#endif //__Win32
+#include <SDL2/SDL_ttf.h>
+#endif
+/* ENVIRONMENT-AGNOSTIC DEFINES */
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_image.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <stdio.h>
+#define TRIGGERS quit_trigger
+#include <henge/trigger.h>
+#include <henge/engine.h>
+
+/* 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
+}
+