added stb, more binaryout changes"
[henge/apc.git] / stb / tests / oversample / stb_wingraph.h
diff --git a/stb/tests/oversample/stb_wingraph.h b/stb/tests/oversample/stb_wingraph.h
new file mode 100644 (file)
index 0000000..94798eb
--- /dev/null
@@ -0,0 +1,829 @@
+// stb_wingraph.h  v0.01 - public domain windows graphics programming
+// wraps WinMain, ChoosePixelFormat, ChangeDisplayResolution, etc. for
+// doing OpenGL graphics
+//
+// in ONE source file, put '#define STB_DEFINE' before including this
+// OR put '#define STB_WINMAIN' to define a WinMain that calls stbwingraph_main(void)
+//
+// @TODO:
+//    2d rendering interface (that can be done easily in software)
+//    STB_WINGRAPH_SOFTWARE -- 2d software rendering only
+//    STB_WINGRAPH_OPENGL   -- OpenGL only
+
+
+#ifndef INCLUDE_STB_WINGRAPH_H
+#define INCLUDE_STB_WINGRAPH_H
+
+#ifdef STB_WINMAIN
+   #ifndef STB_DEFINE
+      #define STB_DEFINE
+      #define STB_WINGRAPH_DISABLE_DEFINE_AT_END
+   #endif
+#endif
+
+#ifdef STB_DEFINE
+   #pragma comment(lib, "opengl32.lib")
+   #pragma comment(lib, "glu32.lib")
+   #pragma comment(lib, "winmm.lib")
+   #pragma comment(lib, "gdi32.lib")
+   #pragma comment(lib, "user32.lib")
+#endif
+
+#ifdef __cplusplus
+#define STB_EXTERN extern "C"
+#else
+#define STB_EXTERN
+#endif
+
+#ifdef STB_DEFINE
+#ifndef _WINDOWS_
+   #ifdef APIENTRY
+   #undef APIENTRY
+   #endif
+   #ifdef WINGDIAPI
+   #undef WINGDIAPI
+   #endif
+   #define _WIN32_WINNT 0x0400  // WM_MOUSEWHEEL
+   #include <windows.h>
+#endif
+#include <stdio.h>
+#include <math.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#endif
+
+typedef void * stbwingraph_hwnd;
+typedef void * stbwingraph_hinstance;
+
+enum
+{
+   STBWINGRAPH_unprocessed = -(1 << 24),
+   STBWINGRAPH_do_not_show,
+   STBWINGRAPH_winproc_exit,
+   STBWINGRAPH_winproc_update,
+   STBWINGRAPH_update_exit,
+   STBWINGRAPH_update_pause,
+};
+
+typedef enum
+{
+   STBWGE__none=0,
+
+   STBWGE_create,
+   STBWGE_create_postshow,
+   STBWGE_draw,
+   STBWGE_destroy,
+   STBWGE_char,
+   STBWGE_keydown,
+   STBWGE_syskeydown,
+   STBWGE_keyup,
+   STBWGE_syskeyup,
+   STBWGE_deactivate,
+   STBWGE_activate,
+   STBWGE_size,
+
+   STBWGE_mousemove ,
+   STBWGE_leftdown  , STBWGE_leftup  ,
+   STBWGE_middledown, STBWGE_middleup,
+   STBWGE_rightdown , STBWGE_rightup ,
+   STBWGE_mousewheel,
+} stbwingraph_event_type;
+
+typedef struct
+{
+   stbwingraph_event_type type;
+
+   // for input events (mouse, keyboard)
+   int mx,my; // mouse x & y
+   int dx,dy;
+   int shift, ctrl, alt;
+
+   // for keyboard events
+   int key;
+
+   // for STBWGE_size:
+   int width, height;
+
+   // for STBWGE_crate
+   int did_share_lists;  // if true, wglShareLists succeeded
+
+   void *handle;
+
+} stbwingraph_event;
+
+typedef int (*stbwingraph_window_proc)(void *data, stbwingraph_event *event);
+
+extern stbwingraph_hinstance   stbwingraph_app;
+extern stbwingraph_hwnd        stbwingraph_primary_window;
+extern int                     stbwingraph_request_fullscreen;
+extern int                     stbwingraph_request_windowed;
+
+STB_EXTERN void stbwingraph_ods(char *str, ...);
+STB_EXTERN int stbwingraph_MessageBox(stbwingraph_hwnd win, unsigned int type,
+                                              char *caption, char *text, ...);
+STB_EXTERN int stbwingraph_ChangeResolution(unsigned int w, unsigned int h,
+                                      unsigned int bits, int use_message_box);
+STB_EXTERN int stbwingraph_SetPixelFormat(stbwingraph_hwnd win, int color_bits,
+            int alpha_bits, int depth_bits, int stencil_bits, int accum_bits);
+STB_EXTERN int stbwingraph_DefineClass(void *hinstance, char *iconname);
+STB_EXTERN void stbwingraph_SwapBuffers(void *win);
+STB_EXTERN void stbwingraph_Priority(int n);
+
+STB_EXTERN void stbwingraph_MakeFonts(void *window, int font_base);
+STB_EXTERN void stbwingraph_ShowWindow(void *window);
+STB_EXTERN void *stbwingraph_CreateWindow(int primary, stbwingraph_window_proc func, void *data, char *text, int width, int height, int fullscreen, int resizeable, int dest_alpha, int stencil);
+STB_EXTERN void *stbwingraph_CreateWindowSimple(stbwingraph_window_proc func, int width, int height);
+STB_EXTERN void *stbwingraph_CreateWindowSimpleFull(stbwingraph_window_proc func, int fullscreen, int ww, int wh, int fw, int fh);
+STB_EXTERN void stbwingraph_DestroyWindow(void *window);
+STB_EXTERN void stbwingraph_ShowCursor(void *window, int visible);
+STB_EXTERN float stbwingraph_GetTimestep(float minimum_time);
+STB_EXTERN void stbwingraph_SetGLWindow(void *win);
+typedef int (*stbwingraph_update)(float timestep, int real, int in_client);
+STB_EXTERN int stbwingraph_MainLoop(stbwingraph_update func, float mintime);
+
+#ifdef STB_DEFINE
+stbwingraph_hinstance   stbwingraph_app;
+stbwingraph_hwnd        stbwingraph_primary_window;
+int stbwingraph_request_fullscreen;
+int stbwingraph_request_windowed;
+
+void stbwingraph_ods(char *str, ...)
+{
+   char buffer[1024];
+   va_list v;
+   va_start(v,str);
+   vsprintf(buffer, str, v);
+   va_end(v);
+   OutputDebugString(buffer);
+}
+
+int stbwingraph_MessageBox(stbwingraph_hwnd win, unsigned int type, char *caption, char *text, ...)
+{
+   va_list v;
+   char buffer[1024];
+   va_start(v, text);
+   vsprintf(buffer, text, v);
+   va_end(v);
+   return MessageBox(win, buffer, caption, type);
+}
+
+void stbwingraph_Priority(int n)
+{
+   int p;
+   switch (n) {
+      case -1: p = THREAD_PRIORITY_BELOW_NORMAL; break;
+      case 0: p = THREAD_PRIORITY_NORMAL; break;
+      case 1: p = THREAD_PRIORITY_ABOVE_NORMAL; break;
+      default:
+         if (n < 0) p = THREAD_PRIORITY_LOWEST;
+         else p = THREAD_PRIORITY_HIGHEST;
+   }
+   SetThreadPriority(GetCurrentThread(), p);
+}
+
+static void stbwingraph_ResetResolution(void)
+{
+   ChangeDisplaySettings(NULL, 0);
+}
+
+static void stbwingraph_RegisterResetResolution(void)
+{
+   static int done=0;
+   if (!done) {
+      done = 1;
+      atexit(stbwingraph_ResetResolution);
+   }
+}
+
+int stbwingraph_ChangeResolution(unsigned int w, unsigned int h, unsigned int bits, int use_message_box)
+{
+   DEVMODE mode;
+   int res;
+   
+   int i, tries=0;
+   for (i=0; ; ++i) {
+      int success = EnumDisplaySettings(NULL, i, &mode);
+      if (!success) break;
+      if (mode.dmBitsPerPel == bits && mode.dmPelsWidth == w && mode.dmPelsHeight == h) {
+         ++tries;
+         success = ChangeDisplaySettings(&mode, CDS_FULLSCREEN); 
+         if (success == DISP_CHANGE_SUCCESSFUL) {
+            stbwingraph_RegisterResetResolution();
+            return TRUE;
+         }
+         break;
+      }
+   }
+
+   if (!tries) {
+      if (use_message_box)
+         stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The resolution %d x %d x %d-bits is not supported.", w, h, bits);
+      return FALSE;
+   }
+
+   // we tried but failed, so try explicitly doing it without specifying refresh rate
+
+   // Win95 support logic
+   mode.dmBitsPerPel = bits; 
+   mode.dmPelsWidth = w; 
+   mode.dmPelsHeight = h; 
+   mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; 
+
+   res = ChangeDisplaySettings(&mode, CDS_FULLSCREEN);
+
+   switch (res) {
+      case DISP_CHANGE_SUCCESSFUL:
+         stbwingraph_RegisterResetResolution();
+         return TRUE;
+
+      case DISP_CHANGE_RESTART:
+         if (use_message_box)
+            stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "Please set your desktop to %d-bit color and then try again.");
+         return FALSE;
+
+      case DISP_CHANGE_FAILED:
+         if (use_message_box)
+            stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The hardware failed to change modes.");
+         return FALSE;
+
+      case DISP_CHANGE_BADMODE:
+         if (use_message_box)
+            stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The resolution %d x %d x %d-bits is not supported.", w, h, bits);
+         return FALSE;
+
+      default:
+         if (use_message_box)
+            stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "An unknown error prevented a change to a %d x %d x %d-bit display.", w, h, bits);
+         return FALSE;
+   }
+}
+
+int stbwingraph_SetPixelFormat(stbwingraph_hwnd win, int color_bits, int alpha_bits, int depth_bits, int stencil_bits, int accum_bits)
+{
+   HDC dc = GetDC(win);
+   PIXELFORMATDESCRIPTOR pfd = { sizeof(pfd) };
+   int                   pixel_format;
+
+   pfd.nVersion = 1;
+   pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
+   pfd.dwLayerMask = PFD_MAIN_PLANE;
+   pfd.iPixelType = PFD_TYPE_RGBA;
+   pfd.cColorBits = color_bits;
+   pfd.cAlphaBits = alpha_bits;
+   pfd.cDepthBits = depth_bits;
+   pfd.cStencilBits = stencil_bits;
+   pfd.cAccumBits = accum_bits;
+
+   pixel_format = ChoosePixelFormat(dc, &pfd);
+   if (!pixel_format) return FALSE;
+
+   if (!DescribePixelFormat(dc, pixel_format, sizeof(PIXELFORMATDESCRIPTOR), &pfd))
+      return FALSE;
+   SetPixelFormat(dc, pixel_format, &pfd);
+
+   return TRUE;
+}
+
+typedef struct
+{
+   // app data
+   stbwingraph_window_proc func;
+   void *data;
+   // creation parameters
+   int   color, alpha, depth, stencil, accum;
+   HWND  share_window;
+   HWND  window;
+   // internal data
+   HGLRC rc;
+   HDC   dc;
+   int   hide_mouse;
+   int   in_client;
+   int   active;
+   int   did_share_lists;
+   int   mx,my; // last mouse positions
+} stbwingraph__window;
+
+static void stbwingraph__inclient(stbwingraph__window *win, int state)
+{
+   if (state != win->in_client) {
+      win->in_client = state;
+      if (win->hide_mouse)
+         ShowCursor(!state);
+   }
+}
+
+static void stbwingraph__key(stbwingraph_event *e, int type, int key, stbwingraph__window *z)
+{
+   e->type  = type;
+   e->key   = key;
+   e->shift = (GetKeyState(VK_SHIFT)   < 0);
+   e->ctrl  = (GetKeyState(VK_CONTROL) < 0);
+   e->alt   = (GetKeyState(VK_MENU)    < 0);
+   if  (z) {
+      e->mx    = z->mx;
+      e->my    = z->my;
+   } else {
+      e->mx = e->my = 0;
+   }
+   e->dx = e->dy = 0;
+}
+
+static void stbwingraph__mouse(stbwingraph_event *e, int type, WPARAM wparam, LPARAM lparam, int capture, void *wnd, stbwingraph__window *z)
+{
+   static int captured = 0;
+   e->type = type;
+   e->mx = (short) LOWORD(lparam);
+   e->my = (short) HIWORD(lparam);
+   if (!z || z->mx == -(1 << 30)) {
+      e->dx = e->dy = 0;
+   } else {
+      e->dx = e->mx - z->mx;
+      e->dy = e->my - z->my;
+   }
+   e->shift = (wparam & MK_SHIFT) != 0;
+   e->ctrl  = (wparam & MK_CONTROL) != 0;
+   e->alt   = (wparam & MK_ALT) != 0;
+   if (z) {
+      z->mx = e->mx;
+      z->my = e->my;
+   }
+   if (capture) {
+      if (!captured && capture == 1)
+         SetCapture(wnd);
+      captured += capture;
+      if (!captured && capture == -1)
+         ReleaseCapture();
+      if (captured < 0) captured = 0;
+   }
+}
+
+static void stbwingraph__mousewheel(stbwingraph_event *e, int type, WPARAM wparam, LPARAM lparam, int capture, void *wnd, stbwingraph__window *z)
+{
+   // lparam seems bogus!
+   static int captured = 0;
+   e->type = type;
+   if (z) {
+      e->mx = z->mx;
+      e->my = z->my;
+   }
+   e->dx = e->dy = 0;
+   e->shift = (wparam & MK_SHIFT) != 0;
+   e->ctrl  = (wparam & MK_CONTROL) != 0;
+   e->alt   = (GetKeyState(VK_MENU)    < 0);
+   e->key = ((int) wparam >> 16);
+}
+
+int stbwingraph_force_update;
+static int WINAPI stbwingraph_WinProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+   int allow_default = TRUE;
+   stbwingraph_event e = { STBWGE__none };
+   // the following line is wrong for 64-bit windows, but VC6 doesn't have GetWindowLongPtr
+   stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(wnd, GWL_USERDATA);
+
+   switch (msg) {
+
+      case WM_CREATE:
+      {
+         LPCREATESTRUCT lpcs = (LPCREATESTRUCT) lparam;
+         assert(z == NULL);
+         z = (stbwingraph__window *) lpcs->lpCreateParams;
+         SetWindowLong(wnd, GWL_USERDATA, (LONG) z);
+         z->dc = GetDC(wnd);
+         if (stbwingraph_SetPixelFormat(wnd, z->color, z->alpha, z->depth, z->stencil, z->accum)) {
+            z->rc = wglCreateContext(z->dc);
+            if (z->rc) {
+               e.type = STBWGE_create;
+               z->did_share_lists = FALSE;
+               if (z->share_window) {
+                  stbwingraph__window *y = (stbwingraph__window *) GetWindowLong(z->share_window, GWL_USERDATA);
+                  if (wglShareLists(z->rc, y->rc))
+                     z->did_share_lists = TRUE;
+               }
+               wglMakeCurrent(z->dc, z->rc);
+               return 0;
+            }
+         }
+         return -1;
+      }
+
+      case WM_PAINT: {
+         PAINTSTRUCT ps;
+         HDC hdc = BeginPaint(wnd, &ps);
+         SelectObject(hdc, GetStockObject(NULL_BRUSH));
+         e.type = STBWGE_draw;
+         e.handle = wnd;
+         z->func(z->data, &e);
+         EndPaint(wnd, &ps);
+         return 0;
+      }
+
+      case WM_DESTROY:
+         e.type = STBWGE_destroy;
+         e.handle = wnd;
+         if (z && z->func)
+            z->func(z->data, &e);
+         wglMakeCurrent(NULL, NULL) ; 
+         if (z) {
+            if (z->rc) wglDeleteContext(z->rc);
+            z->dc = 0;
+            z->rc = 0;
+         }
+         if (wnd == stbwingraph_primary_window)
+            PostQuitMessage (0);
+         return 0;
+
+      case WM_CHAR:         stbwingraph__key(&e, STBWGE_char   , wparam, z); break;
+      case WM_KEYDOWN:      stbwingraph__key(&e, STBWGE_keydown, wparam, z); break;
+      case WM_KEYUP:        stbwingraph__key(&e, STBWGE_keyup  , wparam, z); break;
+
+      case WM_NCMOUSEMOVE:  stbwingraph__inclient(z,0); break;
+      case WM_MOUSEMOVE:    stbwingraph__inclient(z,1); stbwingraph__mouse(&e, STBWGE_mousemove,  wparam, lparam,0,wnd, z); break;
+      case WM_LBUTTONDOWN:  stbwingraph__mouse(&e, STBWGE_leftdown,   wparam, lparam,1,wnd, z); break;
+      case WM_MBUTTONDOWN:  stbwingraph__mouse(&e, STBWGE_middledown, wparam, lparam,1,wnd, z); break;
+      case WM_RBUTTONDOWN:  stbwingraph__mouse(&e, STBWGE_rightdown,  wparam, lparam,1,wnd, z); break;
+      case WM_LBUTTONUP:    stbwingraph__mouse(&e, STBWGE_leftup,     wparam, lparam,-1,wnd, z); break;
+      case WM_MBUTTONUP:    stbwingraph__mouse(&e, STBWGE_middleup,   wparam, lparam,-1,wnd, z); break;
+      case WM_RBUTTONUP:    stbwingraph__mouse(&e, STBWGE_rightup,    wparam, lparam,-1,wnd, z); break;
+      case WM_MOUSEWHEEL:   stbwingraph__mousewheel(&e, STBWGE_mousewheel, wparam, lparam,0,wnd, z); break;
+
+      case WM_ACTIVATE:
+         allow_default = FALSE;
+         if (LOWORD(wparam)==WA_INACTIVE ) {
+            wglMakeCurrent(z->dc, NULL);
+            e.type = STBWGE_deactivate;
+            z->active = FALSE;
+         } else {
+            wglMakeCurrent(z->dc, z->rc);
+            e.type = STBWGE_activate;
+            z->active = TRUE;
+         }
+         e.handle = wnd;
+         z->func(z->data, &e);
+         return 0;
+
+      case WM_SIZE: {
+         RECT rect;
+         allow_default = FALSE;
+         GetClientRect(wnd, &rect);
+         e.type = STBWGE_size;
+         e.width = rect.right;
+         e.height = rect.bottom;
+         e.handle = wnd;
+         z->func(z->data, &e);
+         return 0;
+      }
+
+      default:
+         return DefWindowProc (wnd, msg, wparam, lparam);
+   }
+
+   if (e.type != STBWGE__none) {
+      int n;
+      e.handle = wnd;
+      n = z->func(z->data, &e);
+      if (n == STBWINGRAPH_winproc_exit) {
+         PostQuitMessage(0);
+         n = 0;
+      }
+      if (n == STBWINGRAPH_winproc_update) {
+         stbwingraph_force_update = TRUE;
+         return 1;
+      }
+      if (n != STBWINGRAPH_unprocessed)
+         return n;
+   }
+   return DefWindowProc (wnd, msg, wparam, lparam);
+}
+
+int stbwingraph_DefineClass(HINSTANCE hInstance, char *iconname)
+{
+   WNDCLASSEX  wndclass;
+
+   stbwingraph_app = hInstance;
+
+   wndclass.cbSize        = sizeof(wndclass);
+   wndclass.style         = CS_OWNDC;
+   wndclass.lpfnWndProc   = (WNDPROC) stbwingraph_WinProc;
+   wndclass.cbClsExtra    = 0;
+   wndclass.cbWndExtra    = 0;
+   wndclass.hInstance     = hInstance;
+   wndclass.hIcon         = LoadIcon(hInstance, iconname);
+   wndclass.hCursor       = LoadCursor(NULL,IDC_ARROW);
+   wndclass.hbrBackground = GetStockObject(NULL_BRUSH);
+   wndclass.lpszMenuName  = "zwingraph";
+   wndclass.lpszClassName = "zwingraph";
+   wndclass.hIconSm       = NULL;
+
+   if (!RegisterClassEx(&wndclass))
+      return FALSE;
+   return TRUE;
+}
+
+void stbwingraph_ShowWindow(void *window)
+{
+   stbwingraph_event e = { STBWGE_create_postshow };
+   stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(window, GWL_USERDATA);
+   ShowWindow(window, SW_SHOWNORMAL);
+   InvalidateRect(window, NULL, TRUE);
+   UpdateWindow(window);
+   e.handle = window;
+   z->func(z->data, &e);
+}
+
+void *stbwingraph_CreateWindow(int primary, stbwingraph_window_proc func, void *data, char *text,
+           int width, int height, int fullscreen, int resizeable, int dest_alpha, int stencil)
+{
+   HWND win;
+   DWORD dwstyle;
+   stbwingraph__window *z = (stbwingraph__window *) malloc(sizeof(*z));
+
+   if (z == NULL) return NULL;
+   memset(z, 0, sizeof(*z));
+   z->color = 24;
+   z->depth = 24;
+   z->alpha = dest_alpha;
+   z->stencil = stencil;
+   z->func = func;
+   z->data = data;
+   z->mx = -(1 << 30);
+   z->my = 0;
+
+   if (primary) {
+      if (stbwingraph_request_windowed)
+         fullscreen = FALSE;
+      else if (stbwingraph_request_fullscreen)
+         fullscreen = TRUE;
+   }
+
+   if (fullscreen) {
+      #ifdef STB_SIMPLE
+      stbwingraph_ChangeResolution(width, height, 32, 1);
+      #else
+      if (!stbwingraph_ChangeResolution(width, height, 32, 0))
+         return NULL;
+      #endif
+      dwstyle = WS_POPUP | WS_CLIPSIBLINGS;
+   } else {
+      RECT rect;
+      dwstyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
+      if (resizeable)
+         dwstyle |= WS_SIZEBOX | WS_MAXIMIZEBOX;
+      rect.top = 0;
+      rect.left = 0;
+      rect.right = width;
+      rect.bottom = height;
+      AdjustWindowRect(&rect, dwstyle, FALSE);
+      width = rect.right - rect.left;
+      height = rect.bottom - rect.top;
+   }
+
+   win = CreateWindow("zwingraph", text ? text : "sample", dwstyle,
+                      CW_USEDEFAULT,0, width, height,
+                      NULL, NULL, stbwingraph_app, z);
+
+   if (win == NULL) return win;
+
+   if (primary) {
+      if (stbwingraph_primary_window)
+         stbwingraph_DestroyWindow(stbwingraph_primary_window);
+      stbwingraph_primary_window = win;
+   }
+
+   {
+      stbwingraph_event e = { STBWGE_create };
+      stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA);
+      z->window = win;
+      e.did_share_lists = z->did_share_lists;
+      e.handle = win;
+      if (z->func(z->data, &e) != STBWINGRAPH_do_not_show)
+         stbwingraph_ShowWindow(win);
+   }
+
+   return win;
+}
+
+void *stbwingraph_CreateWindowSimple(stbwingraph_window_proc func, int width, int height)
+{
+   int fullscreen = 0;
+   #ifndef _DEBUG
+   if (width ==  640 && height ==  480) fullscreen = 1;
+   if (width ==  800 && height ==  600) fullscreen = 1;
+   if (width == 1024 && height ==  768) fullscreen = 1;
+   if (width == 1280 && height == 1024) fullscreen = 1;
+   if (width == 1600 && height == 1200) fullscreen = 1;
+   //@TODO: widescreen widths
+   #endif
+   return stbwingraph_CreateWindow(1, func, NULL, NULL, width, height, fullscreen, 1, 0, 0);
+}
+
+void *stbwingraph_CreateWindowSimpleFull(stbwingraph_window_proc func, int fullscreen, int ww, int wh, int fw, int fh)
+{
+   if (fullscreen == -1) {
+   #ifdef _DEBUG
+      fullscreen = 0;
+   #else
+      fullscreen = 1;
+   #endif
+   }
+
+   if (fullscreen) {
+      if (fw) ww = fw;
+      if (fh) wh = fh;
+   }
+   return stbwingraph_CreateWindow(1, func, NULL, NULL, ww, wh, fullscreen, 1, 0, 0);
+}
+
+void stbwingraph_DestroyWindow(void *window)
+{
+   stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(window, GWL_USERDATA);
+   DestroyWindow(window);
+   free(z);
+   if (stbwingraph_primary_window == window)
+      stbwingraph_primary_window = NULL;
+}
+
+void stbwingraph_ShowCursor(void *window, int visible)
+{
+   int hide;
+   stbwingraph__window *win;
+   if (!window)
+      window = stbwingraph_primary_window;
+   win = (stbwingraph__window *) GetWindowLong((HWND) window, GWL_USERDATA);
+   hide = !visible;
+   if (hide != win->hide_mouse) {
+      win->hide_mouse = hide;
+      if (!hide)
+         ShowCursor(TRUE);
+      else if (win->in_client)
+         ShowCursor(FALSE);
+   }
+}
+
+float stbwingraph_GetTimestep(float minimum_time)
+{
+   float elapsedTime;
+   double thisTime;
+   static double lastTime = -1;
+   
+   if (lastTime == -1)
+      lastTime = timeGetTime() / 1000.0 - minimum_time;
+
+   for(;;) {
+      thisTime = timeGetTime() / 1000.0;
+      elapsedTime = (float) (thisTime - lastTime);
+      if (elapsedTime >= minimum_time) {
+         lastTime = thisTime;         
+         return elapsedTime;
+      }
+      #if 1
+      Sleep(2);
+      #endif
+   }
+}
+
+void stbwingraph_SetGLWindow(void *win)
+{
+   stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA);
+   if (z)
+      wglMakeCurrent(z->dc, z->rc);
+}
+
+void stbwingraph_MakeFonts(void *window, int font_base)
+{
+   wglUseFontBitmaps(GetDC(window ? window : stbwingraph_primary_window), 0, 256, font_base);
+}
+
+// returns 1 if WM_QUIT, 0 if 'func' returned 0
+int stbwingraph_MainLoop(stbwingraph_update func, float mintime)
+{
+   int needs_drawing = FALSE;
+   MSG msg;
+
+   int is_animating = TRUE;
+   if (mintime <= 0) mintime = 0.01f;
+
+   for(;;) {
+      int n;
+
+      is_animating = TRUE;
+      // wait for a message if: (a) we're animating and there's already a message
+      // or (b) we're not animating
+      if (!is_animating || PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
+         stbwingraph_force_update = FALSE;
+         if (GetMessage(&msg, NULL, 0, 0)) {
+            TranslateMessage(&msg);
+            DispatchMessage(&msg);
+         } else {
+            return 1;   // WM_QUIT
+         }
+
+         // only force a draw for certain messages...
+         // if I don't do this, we peg at 50% for some reason... must
+         // be a bug somewhere, because we peg at 100% when rendering...
+         // very weird... looks like NVIDIA is pumping some messages
+         // through our pipeline? well, ok, I guess if we can get
+         // non-user-generated messages we have to do this
+         if (!stbwingraph_force_update) {
+            switch (msg.message) {
+               case WM_MOUSEMOVE:
+               case WM_NCMOUSEMOVE:
+                  break;
+               case WM_CHAR:         
+               case WM_KEYDOWN:      
+               case WM_KEYUP:        
+               case WM_LBUTTONDOWN:  
+               case WM_MBUTTONDOWN:  
+               case WM_RBUTTONDOWN:  
+               case WM_LBUTTONUP:    
+               case WM_MBUTTONUP:    
+               case WM_RBUTTONUP:    
+               case WM_TIMER:
+               case WM_SIZE:
+               case WM_ACTIVATE:
+                  needs_drawing = TRUE;
+                  break;
+            }
+         } else
+            needs_drawing = TRUE;
+      }
+
+      // if another message, process that first
+      // @TODO: i don't think this is working, because I can't key ahead
+      // in the SVT demo app
+      if (PeekMessage(&msg, NULL, 0,0, PM_NOREMOVE))
+         continue;
+
+      // and now call update
+      if (needs_drawing || is_animating) {
+         int real=1, in_client=1;
+         if (stbwingraph_primary_window) {
+            stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(stbwingraph_primary_window, GWL_USERDATA);
+            if (z && !z->active) {
+               real = 0;
+            }
+            if (z)
+               in_client = z->in_client;
+         }
+
+         if (stbwingraph_primary_window)
+            stbwingraph_SetGLWindow(stbwingraph_primary_window);
+         n = func(stbwingraph_GetTimestep(mintime), real, in_client);
+         if (n == STBWINGRAPH_update_exit)
+            return 0; // update_quit
+
+         is_animating = (n != STBWINGRAPH_update_pause);
+
+         needs_drawing = FALSE;
+      }
+   }
+}
+
+void stbwingraph_SwapBuffers(void *win)
+{
+   stbwingraph__window *z;
+   if (win == NULL) win = stbwingraph_primary_window;
+   z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA);
+   if (z && z->dc)
+      SwapBuffers(z->dc);
+}
+#endif
+
+#ifdef STB_WINMAIN    
+void stbwingraph_main(void);
+
+char *stb_wingraph_commandline;
+
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
+{
+   {
+      char buffer[1024];
+      // add spaces to either side of the string
+      buffer[0] = ' ';
+      strcpy(buffer+1, lpCmdLine);
+      strcat(buffer, " ");
+      if (strstr(buffer, " -reset ")) {
+         ChangeDisplaySettings(NULL, 0); 
+         exit(0);
+      }
+      if (strstr(buffer, " -window ") || strstr(buffer, " -windowed "))
+         stbwingraph_request_windowed = TRUE;
+      else if (strstr(buffer, " -full ") || strstr(buffer, " -fullscreen "))
+         stbwingraph_request_fullscreen = TRUE;
+   }
+   stb_wingraph_commandline = lpCmdLine;
+
+   stbwingraph_DefineClass(hInstance, "appicon");
+   stbwingraph_main();
+
+   return 0;
+}
+#endif
+
+#undef STB_EXTERN
+#ifdef STB_WINGRAPH_DISABLE_DEFINE_AT_END
+#undef STB_DEFINE
+#endif
+
+#endif // INCLUDE_STB_WINGRAPH_H