grammar proto1
[henge/webcc.git] / src / core / main.c
index b8a8e12..a14ce6a 100644 (file)
@@ -2,91 +2,79 @@
   \brief   engine entry
   \details initializes necessary subsystems before invoking the preloader,
            which loads initial game data, before finally invoking the
-           main loop gameloop(void)
-  \author  Ken
+           main loop main_loop(void), which may be run in either blocking
+           or non-blocking mode.
+  \author  Mihrtec
   \date    2016
 ------------------------------------------------------------------------------*/
-#ifdef __Win32
-#include <windows.h>
-#endif
 #ifdef __EMSCRIPTEN__
+/* Web Environment */
 #define main em_main
+#include <SDL_ttf.h>
 #include <emscripten/emscripten.h>
 #else
-#define INFINITE_MAINLOOP
+/* 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 <SDL_ttf.h>
 #include <stdint.h>
 #include <setjmp.h>
 #include <stdio.h>
+#define TRIGGERS quit_trigger
+#include <core/trigger.h>
+#include <core/engine.h>
 
-/* debug level */
-#define DEBUG 1
-
-/* Exit Codes */
-#define EXIT_GRACEFUL 1
-#define EXIT_DEBUG 2
-#define EXIT_PANIC 3
-
-/* Target frames per second */
-#ifndef TARGET_FPS
-#define TARGET_FPS 60
-#endif
-
-/* Target milliseconds per frame */
-#define TARGET_DT (1000 / TARGET_FPS)
-
-/* precompiler round-up division */
-#define DIV_UP(x,y) ((x + (y / 2)) / y)
-
-/* internally exposed functions */
-int  main_init(void);
+/* exposed functions */
 void main_loop(void);
 
-/* unexposed externs *
+/* 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);
+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);*/
-
-/* Function trigger stack for "unwinding" initialization of modules */
-#define MAX_TRIGGERS 16
-struct call_trigger
-{ int num_funcs;
-  void (*func[MAX_TRIGGERS])(void);
-} quit_trigger = {0};
-
-#define TRIGGER_SET(TARG, FUNC) TARG.func[TARG.num_funcs++] = FUNC
-#define TRIGGER(TARG) while(TARG.num_funcs) (TARG.func[--TARG.num_funcs])()
+extern void        io_quit(void);
+#endif //TODO
 
 /* main jump buffer */
 jmp_buf jmp_main;
 
-/** initializes subsystems and calls main_loop(void)
-  @return (exit_status - 1), where a normal shutdown's exit_status = 1.
+/*@
+  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
-*******************************************************************************/
-int main (int argc, char** argv)
+*/
+#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(); /* no return during native execution */
-#ifdef __EMSCRIPTEN__
-        emscripten_set_main_loop(main_loop,0,0);
-        return 0;
-#endif
+        main_loop();
       case EXIT_GRACEFUL:
         break;
       default:
@@ -98,95 +86,86 @@ int main (int argc, char** argv)
   return 0;
 }
 
-/** initializer
+#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
-*******************************************************************************/
-int main_init()
-{ static char bInitialized = 0;
+*/
+#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++)
-    { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Resetting [%d]\n", bInitialized);
-      return 0;
+    { TRIGGER(quit_trigger);
+      SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Resetting [%d]\n",
+                  bInitialized);
     }
 
-  if (SDL_Init(SDL_INIT_EVERYTHING & ~(SDL_INIT_TIMER | SDL_INIT_HAPTIC)) < 0)
-    { fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError());
-      return - 1;
-    }
-  TRIGGER_SET(quit_trigger, SDL_Quit);
+  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);
 
-  if (IMG_Init(IMG_INIT_PNG) != IMG_INIT_PNG)
-    { fprintf(stderr, "IMG_Init failed: %s\n", IMG_GetError());
-      return -2;
-    }
-  TRIGGER_SET(quit_trigger, IMG_Quit);
-
-  if (TTF_Init() == -1)
-    { fprintf(stderr, "TTF_Init failed: %s\n", TTF_GetError());
-      return -3;
-    }
-  TRIGGER_SET(quit_trigger, TTF_Quit);
-  /*
-  if (state_init())
-  { printf("state_init failed: %s\n", state_get_error());
-    return -4;
-  }
-  TRIGGER_SET(quit_trigger, state_quit);
-  if (renderer_init())
-  { printf("state_init failed: %s\n", renderer_get_error());
-    return -5;
-  }
-  TRIGGER_SET(quit_trigger, renderer_quit);
-  if (io_init())
-  { printf("io_init failed: %s\n", io_get_error());
-    return -6;
-  }
-  TRIGGER_SET(quit_trigger, io_quit);
-  */
   SDL_Log("Initialization Complete.");
   return 0;
 }
 
-/** main loop.
-  exits only during core_shutdown and core_panic if in an infinite loop
-*******************************************************************************/
-void main_loop()
-{ static uint32_t main_loop_last_ticks = 0;
-  static SDL_Event event;
+/*@
+  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:
-  /* Poll events */
+#endif
+  /* user/system inputs */
   while (SDL_PollEvent(&event))
-    //state_handle_event(&event);
-    ;
+      state_handle_event(&event);
 
   /* change in time since last loop */
-  delta_ticks = SDL_GetTicks() - main_loop_last_ticks;
-
+  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 forward by the change in time */
-  //state_tick(delta_ticks);
-  main_loop_last_ticks = SDL_GetTicks();
-
-  /* render the scene to backbuffer */
-////todo...
-  /* swap in the backbuffer for display */
-////todo...
-
-  /* cap the framerate if we're handling the loop mechanism directly, but only
-     yield if we have a substantial portion of time to yield (1/10th of the
-     target delta) */
-  printf("%d\n", main_loop_last_ticks);
-#ifdef INFINITE_MAINLOOP
-  delta_ticks = SDL_GetTicks() - main_loop_last_ticks;
-  if ((delta_ticks + DIV_UP(TARGET_DT, 10)) < TARGET_DT)
+    { 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
 }
+