text: // 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
  