1 // stb_wingraph.h  v0.01 - public domain windows graphics programming
2 // wraps WinMain, ChoosePixelFormat, ChangeDisplayResolution, etc. for
3 // doing OpenGL graphics
4 //
5 // in ONE source file, put '#define STB_DEFINE' before including this
6 // OR put '#define STB_WINMAIN' to define a WinMain that calls stbwingraph_main(void)
7 //
8 // @TODO:
9 //    2d rendering interface (that can be done easily in software)
10 //    STB_WINGRAPH_SOFTWARE -- 2d software rendering only
11 //    STB_WINGRAPH_OPENGL   -- OpenGL only
12 
13 
14 #ifndef INCLUDE_STB_WINGRAPH_H
15 #define INCLUDE_STB_WINGRAPH_H
16 
17 #ifdef STB_WINMAIN
18    #ifndef STB_DEFINE
19       #define STB_DEFINE
20       #define STB_WINGRAPH_DISABLE_DEFINE_AT_END
21    #endif
22 #endif
23 
24 #ifdef STB_DEFINE
25    #pragma comment(lib, "opengl32.lib")
26    #pragma comment(lib, "glu32.lib")
27    #pragma comment(lib, "winmm.lib")
28    #pragma comment(lib, "gdi32.lib")
29    #pragma comment(lib, "user32.lib")
30 #endif
31 
32 #ifdef __cplusplus
33 #define STB_EXTERN extern "C"
34 #else
35 #define STB_EXTERN
36 #endif
37 
38 #ifdef STB_DEFINE
39 #ifndef _WINDOWS_
40    #ifdef APIENTRY
41    #undef APIENTRY
42    #endif
43    #ifdef WINGDIAPI
44    #undef WINGDIAPI
45    #endif
46    #define _WIN32_WINNT 0x0400  // WM_MOUSEWHEEL
47    #include <windows.h>
48 #endif
49 #include <stdio.h>
50 #include <math.h>
51 #include <time.h>
52 #include <string.h>
53 #include <assert.h>
54 #endif
55 
56 typedef void * stbwingraph_hwnd;
57 typedef void * stbwingraph_hinstance;
58 
59 enum
60 {
61    STBWINGRAPH_unprocessed = -(1 << 24),
62    STBWINGRAPH_do_not_show,
63    STBWINGRAPH_winproc_exit,
64    STBWINGRAPH_winproc_update,
65    STBWINGRAPH_update_exit,
66    STBWINGRAPH_update_pause,
67 };
68 
69 typedef enum
70 {
71    STBWGE__none=0,
72 
73    STBWGE_create,
74    STBWGE_create_postshow,
75    STBWGE_draw,
76    STBWGE_destroy,
77    STBWGE_char,
78    STBWGE_keydown,
79    STBWGE_syskeydown,
80    STBWGE_keyup,
81    STBWGE_syskeyup,
82    STBWGE_deactivate,
83    STBWGE_activate,
84    STBWGE_size,
85 
86    STBWGE_mousemove ,
87    STBWGE_leftdown  , STBWGE_leftup  ,
88    STBWGE_middledown, STBWGE_middleup,
89    STBWGE_rightdown , STBWGE_rightup ,
90    STBWGE_mousewheel,
91 } stbwingraph_event_type;
92 
93 typedef struct
94 {
95    stbwingraph_event_type type;
96 
97    // for input events (mouse, keyboard)
98    int mx,my; // mouse x & y
99    int dx,dy;
100    int shift, ctrl, alt;
101 
102    // for keyboard events
103    int key;
104 
105    // for STBWGE_size:
106    int width, height;
107 
108    // for STBWGE_crate
109    int did_share_lists;  // if true, wglShareLists succeeded
110 
111    void *handle;
112 
113 } stbwingraph_event;
114 
115 typedef int (*stbwingraph_window_proc)(void *data, stbwingraph_event *event);
116 
117 extern stbwingraph_hinstance   stbwingraph_app;
118 extern stbwingraph_hwnd        stbwingraph_primary_window;
119 extern int                     stbwingraph_request_fullscreen;
120 extern int                     stbwingraph_request_windowed;
121 
122 STB_EXTERN void stbwingraph_ods(char *str, ...);
123 STB_EXTERN int stbwingraph_MessageBox(stbwingraph_hwnd win, unsigned int type,
124                                               char *caption, char *text, ...);
125 STB_EXTERN int stbwingraph_ChangeResolution(unsigned int w, unsigned int h,
126                                       unsigned int bits, int use_message_box);
127 STB_EXTERN int stbwingraph_SetPixelFormat(stbwingraph_hwnd win, int color_bits,
128             int alpha_bits, int depth_bits, int stencil_bits, int accum_bits);
129 STB_EXTERN int stbwingraph_DefineClass(void *hinstance, char *iconname);
130 STB_EXTERN void stbwingraph_SwapBuffers(void *win);
131 STB_EXTERN void stbwingraph_Priority(int n);
132 
133 STB_EXTERN void stbwingraph_MakeFonts(void *window, int font_base);
134 STB_EXTERN void stbwingraph_ShowWindow(void *window);
135 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);
136 STB_EXTERN void *stbwingraph_CreateWindowSimple(stbwingraph_window_proc func, int width, int height);
137 STB_EXTERN void *stbwingraph_CreateWindowSimpleFull(stbwingraph_window_proc func, int fullscreen, int ww, int wh, int fw, int fh);
138 STB_EXTERN void stbwingraph_DestroyWindow(void *window);
139 STB_EXTERN void stbwingraph_ShowCursor(void *window, int visible);
140 STB_EXTERN float stbwingraph_GetTimestep(float minimum_time);
141 STB_EXTERN void stbwingraph_SetGLWindow(void *win);
142 typedef int (*stbwingraph_update)(float timestep, int real, int in_client);
143 STB_EXTERN int stbwingraph_MainLoop(stbwingraph_update func, float mintime);
144 
145 #ifdef STB_DEFINE
146 stbwingraph_hinstance   stbwingraph_app;
147 stbwingraph_hwnd        stbwingraph_primary_window;
148 int stbwingraph_request_fullscreen;
149 int stbwingraph_request_windowed;
150 
stbwingraph_ods(char * str,...)151 void stbwingraph_ods(char *str, ...)
152 {
153    char buffer[1024];
154    va_list v;
155    va_start(v,str);
156    vsprintf(buffer, str, v);
157    va_end(v);
158    OutputDebugString(buffer);
159 }
160 
stbwingraph_MessageBox(stbwingraph_hwnd win,unsigned int type,char * caption,char * text,...)161 int stbwingraph_MessageBox(stbwingraph_hwnd win, unsigned int type, char *caption, char *text, ...)
162 {
163    va_list v;
164    char buffer[1024];
165    va_start(v, text);
166    vsprintf(buffer, text, v);
167    va_end(v);
168    return MessageBox(win, buffer, caption, type);
169 }
170 
stbwingraph_Priority(int n)171 void stbwingraph_Priority(int n)
172 {
173    int p;
174    switch (n) {
175       case -1: p = THREAD_PRIORITY_BELOW_NORMAL; break;
176       case 0: p = THREAD_PRIORITY_NORMAL; break;
177       case 1: p = THREAD_PRIORITY_ABOVE_NORMAL; break;
178       default:
179          if (n < 0) p = THREAD_PRIORITY_LOWEST;
180          else p = THREAD_PRIORITY_HIGHEST;
181    }
182    SetThreadPriority(GetCurrentThread(), p);
183 }
184 
stbwingraph_ResetResolution(void)185 static void stbwingraph_ResetResolution(void)
186 {
187    ChangeDisplaySettings(NULL, 0);
188 }
189 
stbwingraph_RegisterResetResolution(void)190 static void stbwingraph_RegisterResetResolution(void)
191 {
192    static int done=0;
193    if (!done) {
194       done = 1;
195       atexit(stbwingraph_ResetResolution);
196    }
197 }
198 
stbwingraph_ChangeResolution(unsigned int w,unsigned int h,unsigned int bits,int use_message_box)199 int stbwingraph_ChangeResolution(unsigned int w, unsigned int h, unsigned int bits, int use_message_box)
200 {
201    DEVMODE mode;
202    int res;
203 
204    int i, tries=0;
205    for (i=0; ; ++i) {
206       int success = EnumDisplaySettings(NULL, i, &mode);
207       if (!success) break;
208       if (mode.dmBitsPerPel == bits && mode.dmPelsWidth == w && mode.dmPelsHeight == h) {
209          ++tries;
210          success = ChangeDisplaySettings(&mode, CDS_FULLSCREEN);
211          if (success == DISP_CHANGE_SUCCESSFUL) {
212             stbwingraph_RegisterResetResolution();
213             return TRUE;
214          }
215          break;
216       }
217    }
218 
219    if (!tries) {
220       if (use_message_box)
221          stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The resolution %d x %d x %d-bits is not supported.", w, h, bits);
222       return FALSE;
223    }
224 
225    // we tried but failed, so try explicitly doing it without specifying refresh rate
226 
227    // Win95 support logic
228    mode.dmBitsPerPel = bits;
229    mode.dmPelsWidth = w;
230    mode.dmPelsHeight = h;
231    mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
232 
233    res = ChangeDisplaySettings(&mode, CDS_FULLSCREEN);
234 
235    switch (res) {
236       case DISP_CHANGE_SUCCESSFUL:
237          stbwingraph_RegisterResetResolution();
238          return TRUE;
239 
240       case DISP_CHANGE_RESTART:
241          if (use_message_box)
242             stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "Please set your desktop to %d-bit color and then try again.");
243          return FALSE;
244 
245       case DISP_CHANGE_FAILED:
246          if (use_message_box)
247             stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The hardware failed to change modes.");
248          return FALSE;
249 
250       case DISP_CHANGE_BADMODE:
251          if (use_message_box)
252             stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The resolution %d x %d x %d-bits is not supported.", w, h, bits);
253          return FALSE;
254 
255       default:
256          if (use_message_box)
257             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);
258          return FALSE;
259    }
260 }
261 
stbwingraph_SetPixelFormat(stbwingraph_hwnd win,int color_bits,int alpha_bits,int depth_bits,int stencil_bits,int accum_bits)262 int stbwingraph_SetPixelFormat(stbwingraph_hwnd win, int color_bits, int alpha_bits, int depth_bits, int stencil_bits, int accum_bits)
263 {
264    HDC dc = GetDC(win);
265    PIXELFORMATDESCRIPTOR pfd = { sizeof(pfd) };
266    int                   pixel_format;
267 
268    pfd.nVersion = 1;
269    pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
270    pfd.dwLayerMask = PFD_MAIN_PLANE;
271    pfd.iPixelType = PFD_TYPE_RGBA;
272    pfd.cColorBits = color_bits;
273    pfd.cAlphaBits = alpha_bits;
274    pfd.cDepthBits = depth_bits;
275    pfd.cStencilBits = stencil_bits;
276    pfd.cAccumBits = accum_bits;
277 
278    pixel_format = ChoosePixelFormat(dc, &pfd);
279    if (!pixel_format) return FALSE;
280 
281    if (!DescribePixelFormat(dc, pixel_format, sizeof(PIXELFORMATDESCRIPTOR), &pfd))
282       return FALSE;
283    SetPixelFormat(dc, pixel_format, &pfd);
284 
285    return TRUE;
286 }
287 
288 typedef struct
289 {
290    // app data
291    stbwingraph_window_proc func;
292    void *data;
293    // creation parameters
294    int   color, alpha, depth, stencil, accum;
295    HWND  share_window;
296    HWND  window;
297    // internal data
298    HGLRC rc;
299    HDC   dc;
300    int   hide_mouse;
301    int   in_client;
302    int   active;
303    int   did_share_lists;
304    int   mx,my; // last mouse positions
305 } stbwingraph__window;
306 
stbwingraph__inclient(stbwingraph__window * win,int state)307 static void stbwingraph__inclient(stbwingraph__window *win, int state)
308 {
309    if (state != win->in_client) {
310       win->in_client = state;
311       if (win->hide_mouse)
312          ShowCursor(!state);
313    }
314 }
315 
stbwingraph__key(stbwingraph_event * e,int type,int key,stbwingraph__window * z)316 static void stbwingraph__key(stbwingraph_event *e, int type, int key, stbwingraph__window *z)
317 {
318    e->type  = type;
319    e->key   = key;
320    e->shift = (GetKeyState(VK_SHIFT)   < 0);
321    e->ctrl  = (GetKeyState(VK_CONTROL) < 0);
322    e->alt   = (GetKeyState(VK_MENU)    < 0);
323    if  (z) {
324       e->mx    = z->mx;
325       e->my    = z->my;
326    } else {
327       e->mx = e->my = 0;
328    }
329    e->dx = e->dy = 0;
330 }
331 
stbwingraph__mouse(stbwingraph_event * e,int type,WPARAM wparam,LPARAM lparam,int capture,void * wnd,stbwingraph__window * z)332 static void stbwingraph__mouse(stbwingraph_event *e, int type, WPARAM wparam, LPARAM lparam, int capture, void *wnd, stbwingraph__window *z)
333 {
334    static int captured = 0;
335    e->type = type;
336    e->mx = (short) LOWORD(lparam);
337    e->my = (short) HIWORD(lparam);
338    if (!z || z->mx == -(1 << 30)) {
339       e->dx = e->dy = 0;
340    } else {
341       e->dx = e->mx - z->mx;
342       e->dy = e->my - z->my;
343    }
344    e->shift = (wparam & MK_SHIFT) != 0;
345    e->ctrl  = (wparam & MK_CONTROL) != 0;
346    e->alt   = (wparam & MK_ALT) != 0;
347    if (z) {
348       z->mx = e->mx;
349       z->my = e->my;
350    }
351    if (capture) {
352       if (!captured && capture == 1)
353          SetCapture(wnd);
354       captured += capture;
355       if (!captured && capture == -1)
356          ReleaseCapture();
357       if (captured < 0) captured = 0;
358    }
359 }
360 
stbwingraph__mousewheel(stbwingraph_event * e,int type,WPARAM wparam,LPARAM lparam,int capture,void * wnd,stbwingraph__window * z)361 static void stbwingraph__mousewheel(stbwingraph_event *e, int type, WPARAM wparam, LPARAM lparam, int capture, void *wnd, stbwingraph__window *z)
362 {
363    // lparam seems bogus!
364    static int captured = 0;
365    e->type = type;
366    if (z) {
367       e->mx = z->mx;
368       e->my = z->my;
369    }
370    e->dx = e->dy = 0;
371    e->shift = (wparam & MK_SHIFT) != 0;
372    e->ctrl  = (wparam & MK_CONTROL) != 0;
373    e->alt   = (GetKeyState(VK_MENU)    < 0);
374    e->key = ((int) wparam >> 16);
375 }
376 
377 int stbwingraph_force_update;
stbwingraph_WinProc(HWND wnd,UINT msg,WPARAM wparam,LPARAM lparam)378 static int WINAPI stbwingraph_WinProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
379 {
380    int allow_default = TRUE;
381    stbwingraph_event e = { STBWGE__none };
382    // the following line is wrong for 64-bit windows, but VC6 doesn't have GetWindowLongPtr
383    stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(wnd, GWL_USERDATA);
384 
385    switch (msg) {
386 
387       case WM_CREATE:
388       {
389          LPCREATESTRUCT lpcs = (LPCREATESTRUCT) lparam;
390          assert(z == NULL);
391          z = (stbwingraph__window *) lpcs->lpCreateParams;
392          SetWindowLong(wnd, GWL_USERDATA, (LONG) z);
393          z->dc = GetDC(wnd);
394          if (stbwingraph_SetPixelFormat(wnd, z->color, z->alpha, z->depth, z->stencil, z->accum)) {
395             z->rc = wglCreateContext(z->dc);
396             if (z->rc) {
397                e.type = STBWGE_create;
398                z->did_share_lists = FALSE;
399                if (z->share_window) {
400                   stbwingraph__window *y = (stbwingraph__window *) GetWindowLong(z->share_window, GWL_USERDATA);
401                   if (wglShareLists(z->rc, y->rc))
402                      z->did_share_lists = TRUE;
403                }
404                wglMakeCurrent(z->dc, z->rc);
405                return 0;
406             }
407          }
408          return -1;
409       }
410 
411       case WM_PAINT: {
412          PAINTSTRUCT ps;
413          HDC hdc = BeginPaint(wnd, &ps);
414          SelectObject(hdc, GetStockObject(NULL_BRUSH));
415          e.type = STBWGE_draw;
416          e.handle = wnd;
417          z->func(z->data, &e);
418          EndPaint(wnd, &ps);
419          return 0;
420       }
421 
422       case WM_DESTROY:
423          e.type = STBWGE_destroy;
424          e.handle = wnd;
425          if (z && z->func)
426             z->func(z->data, &e);
427          wglMakeCurrent(NULL, NULL) ;
428          if (z) {
429             if (z->rc) wglDeleteContext(z->rc);
430             z->dc = 0;
431             z->rc = 0;
432          }
433          if (wnd == stbwingraph_primary_window)
434             PostQuitMessage (0);
435          return 0;
436 
437       case WM_CHAR:         stbwingraph__key(&e, STBWGE_char   , wparam, z); break;
438       case WM_KEYDOWN:      stbwingraph__key(&e, STBWGE_keydown, wparam, z); break;
439       case WM_KEYUP:        stbwingraph__key(&e, STBWGE_keyup  , wparam, z); break;
440 
441       case WM_NCMOUSEMOVE:  stbwingraph__inclient(z,0); break;
442       case WM_MOUSEMOVE:    stbwingraph__inclient(z,1); stbwingraph__mouse(&e, STBWGE_mousemove,  wparam, lparam,0,wnd, z); break;
443       case WM_LBUTTONDOWN:  stbwingraph__mouse(&e, STBWGE_leftdown,   wparam, lparam,1,wnd, z); break;
444       case WM_MBUTTONDOWN:  stbwingraph__mouse(&e, STBWGE_middledown, wparam, lparam,1,wnd, z); break;
445       case WM_RBUTTONDOWN:  stbwingraph__mouse(&e, STBWGE_rightdown,  wparam, lparam,1,wnd, z); break;
446       case WM_LBUTTONUP:    stbwingraph__mouse(&e, STBWGE_leftup,     wparam, lparam,-1,wnd, z); break;
447       case WM_MBUTTONUP:    stbwingraph__mouse(&e, STBWGE_middleup,   wparam, lparam,-1,wnd, z); break;
448       case WM_RBUTTONUP:    stbwingraph__mouse(&e, STBWGE_rightup,    wparam, lparam,-1,wnd, z); break;
449       case WM_MOUSEWHEEL:   stbwingraph__mousewheel(&e, STBWGE_mousewheel, wparam, lparam,0,wnd, z); break;
450 
451       case WM_ACTIVATE:
452          allow_default = FALSE;
453          if (LOWORD(wparam)==WA_INACTIVE ) {
454             wglMakeCurrent(z->dc, NULL);
455             e.type = STBWGE_deactivate;
456             z->active = FALSE;
457          } else {
458             wglMakeCurrent(z->dc, z->rc);
459             e.type = STBWGE_activate;
460             z->active = TRUE;
461          }
462          e.handle = wnd;
463          z->func(z->data, &e);
464          return 0;
465 
466       case WM_SIZE: {
467          RECT rect;
468          allow_default = FALSE;
469          GetClientRect(wnd, &rect);
470          e.type = STBWGE_size;
471          e.width = rect.right;
472          e.height = rect.bottom;
473          e.handle = wnd;
474          z->func(z->data, &e);
475          return 0;
476       }
477 
478       default:
479          return DefWindowProc (wnd, msg, wparam, lparam);
480    }
481 
482    if (e.type != STBWGE__none) {
483       int n;
484       e.handle = wnd;
485       n = z->func(z->data, &e);
486       if (n == STBWINGRAPH_winproc_exit) {
487          PostQuitMessage(0);
488          n = 0;
489       }
490       if (n == STBWINGRAPH_winproc_update) {
491          stbwingraph_force_update = TRUE;
492          return 1;
493       }
494       if (n != STBWINGRAPH_unprocessed)
495          return n;
496    }
497    return DefWindowProc (wnd, msg, wparam, lparam);
498 }
499 
stbwingraph_DefineClass(HINSTANCE hInstance,char * iconname)500 int stbwingraph_DefineClass(HINSTANCE hInstance, char *iconname)
501 {
502    WNDCLASSEX  wndclass;
503 
504    stbwingraph_app = hInstance;
505 
506    wndclass.cbSize        = sizeof(wndclass);
507    wndclass.style         = CS_OWNDC;
508    wndclass.lpfnWndProc   = (WNDPROC) stbwingraph_WinProc;
509    wndclass.cbClsExtra    = 0;
510    wndclass.cbWndExtra    = 0;
511    wndclass.hInstance     = hInstance;
512    wndclass.hIcon         = LoadIcon(hInstance, iconname);
513    wndclass.hCursor       = LoadCursor(NULL,IDC_ARROW);
514    wndclass.hbrBackground = GetStockObject(NULL_BRUSH);
515    wndclass.lpszMenuName  = "zwingraph";
516    wndclass.lpszClassName = "zwingraph";
517    wndclass.hIconSm       = NULL;
518 
519    if (!RegisterClassEx(&wndclass))
520       return FALSE;
521    return TRUE;
522 }
523 
stbwingraph_ShowWindow(void * window)524 void stbwingraph_ShowWindow(void *window)
525 {
526    stbwingraph_event e = { STBWGE_create_postshow };
527    stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(window, GWL_USERDATA);
528    ShowWindow(window, SW_SHOWNORMAL);
529    InvalidateRect(window, NULL, TRUE);
530    UpdateWindow(window);
531    e.handle = window;
532    z->func(z->data, &e);
533 }
534 
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)535 void *stbwingraph_CreateWindow(int primary, stbwingraph_window_proc func, void *data, char *text,
536            int width, int height, int fullscreen, int resizeable, int dest_alpha, int stencil)
537 {
538    HWND win;
539    DWORD dwstyle;
540    stbwingraph__window *z = (stbwingraph__window *) malloc(sizeof(*z));
541 
542    if (z == NULL) return NULL;
543    memset(z, 0, sizeof(*z));
544    z->color = 24;
545    z->depth = 24;
546    z->alpha = dest_alpha;
547    z->stencil = stencil;
548    z->func = func;
549    z->data = data;
550    z->mx = -(1 << 30);
551    z->my = 0;
552 
553    if (primary) {
554       if (stbwingraph_request_windowed)
555          fullscreen = FALSE;
556       else if (stbwingraph_request_fullscreen)
557          fullscreen = TRUE;
558    }
559 
560    if (fullscreen) {
561       #ifdef STB_SIMPLE
562       stbwingraph_ChangeResolution(width, height, 32, 1);
563       #else
564       if (!stbwingraph_ChangeResolution(width, height, 32, 0))
565          return NULL;
566       #endif
567       dwstyle = WS_POPUP | WS_CLIPSIBLINGS;
568    } else {
569       RECT rect;
570       dwstyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
571       if (resizeable)
572          dwstyle |= WS_SIZEBOX | WS_MAXIMIZEBOX;
573       rect.top = 0;
574       rect.left = 0;
575       rect.right = width;
576       rect.bottom = height;
577       AdjustWindowRect(&rect, dwstyle, FALSE);
578       width = rect.right - rect.left;
579       height = rect.bottom - rect.top;
580    }
581 
582    win = CreateWindow("zwingraph", text ? text : "sample", dwstyle,
583                       CW_USEDEFAULT,0, width, height,
584                       NULL, NULL, stbwingraph_app, z);
585 
586    if (win == NULL) return win;
587 
588    if (primary) {
589       if (stbwingraph_primary_window)
590          stbwingraph_DestroyWindow(stbwingraph_primary_window);
591       stbwingraph_primary_window = win;
592    }
593 
594    {
595       stbwingraph_event e = { STBWGE_create };
596       stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA);
597       z->window = win;
598       e.did_share_lists = z->did_share_lists;
599       e.handle = win;
600       if (z->func(z->data, &e) != STBWINGRAPH_do_not_show)
601          stbwingraph_ShowWindow(win);
602    }
603 
604    return win;
605 }
606 
stbwingraph_CreateWindowSimple(stbwingraph_window_proc func,int width,int height)607 void *stbwingraph_CreateWindowSimple(stbwingraph_window_proc func, int width, int height)
608 {
609    int fullscreen = 0;
610    #ifndef _DEBUG
611    if (width ==  640 && height ==  480) fullscreen = 1;
612    if (width ==  800 && height ==  600) fullscreen = 1;
613    if (width == 1024 && height ==  768) fullscreen = 1;
614    if (width == 1280 && height == 1024) fullscreen = 1;
615    if (width == 1600 && height == 1200) fullscreen = 1;
616    //@TODO: widescreen widths
617    #endif
618    return stbwingraph_CreateWindow(1, func, NULL, NULL, width, height, fullscreen, 1, 0, 0);
619 }
620 
stbwingraph_CreateWindowSimpleFull(stbwingraph_window_proc func,int fullscreen,int ww,int wh,int fw,int fh)621 void *stbwingraph_CreateWindowSimpleFull(stbwingraph_window_proc func, int fullscreen, int ww, int wh, int fw, int fh)
622 {
623    if (fullscreen == -1) {
624    #ifdef _DEBUG
625       fullscreen = 0;
626    #else
627       fullscreen = 1;
628    #endif
629    }
630 
631    if (fullscreen) {
632       if (fw) ww = fw;
633       if (fh) wh = fh;
634    }
635    return stbwingraph_CreateWindow(1, func, NULL, NULL, ww, wh, fullscreen, 1, 0, 0);
636 }
637 
stbwingraph_DestroyWindow(void * window)638 void stbwingraph_DestroyWindow(void *window)
639 {
640    stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(window, GWL_USERDATA);
641    DestroyWindow(window);
642    free(z);
643    if (stbwingraph_primary_window == window)
644       stbwingraph_primary_window = NULL;
645 }
646 
stbwingraph_ShowCursor(void * window,int visible)647 void stbwingraph_ShowCursor(void *window, int visible)
648 {
649    int hide;
650    stbwingraph__window *win;
651    if (!window)
652       window = stbwingraph_primary_window;
653    win = (stbwingraph__window *) GetWindowLong((HWND) window, GWL_USERDATA);
654    hide = !visible;
655    if (hide != win->hide_mouse) {
656       win->hide_mouse = hide;
657       if (!hide)
658          ShowCursor(TRUE);
659       else if (win->in_client)
660          ShowCursor(FALSE);
661    }
662 }
663 
stbwingraph_GetTimestep(float minimum_time)664 float stbwingraph_GetTimestep(float minimum_time)
665 {
666    float elapsedTime;
667    double thisTime;
668    static double lastTime = -1;
669 
670    if (lastTime == -1)
671       lastTime = timeGetTime() / 1000.0 - minimum_time;
672 
673    for(;;) {
674       thisTime = timeGetTime() / 1000.0;
675       elapsedTime = (float) (thisTime - lastTime);
676       if (elapsedTime >= minimum_time) {
677          lastTime = thisTime;
678          return elapsedTime;
679       }
680       #if 1
681       Sleep(2);
682       #endif
683    }
684 }
685 
stbwingraph_SetGLWindow(void * win)686 void stbwingraph_SetGLWindow(void *win)
687 {
688    stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA);
689    if (z)
690       wglMakeCurrent(z->dc, z->rc);
691 }
692 
stbwingraph_MakeFonts(void * window,int font_base)693 void stbwingraph_MakeFonts(void *window, int font_base)
694 {
695    wglUseFontBitmaps(GetDC(window ? window : stbwingraph_primary_window), 0, 256, font_base);
696 }
697 
698 // returns 1 if WM_QUIT, 0 if 'func' returned 0
stbwingraph_MainLoop(stbwingraph_update func,float mintime)699 int stbwingraph_MainLoop(stbwingraph_update func, float mintime)
700 {
701    int needs_drawing = FALSE;
702    MSG msg;
703 
704    int is_animating = TRUE;
705    if (mintime <= 0) mintime = 0.01f;
706 
707    for(;;) {
708       int n;
709 
710       is_animating = TRUE;
711       // wait for a message if: (a) we're animating and there's already a message
712       // or (b) we're not animating
713       if (!is_animating || PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
714          stbwingraph_force_update = FALSE;
715          if (GetMessage(&msg, NULL, 0, 0)) {
716             TranslateMessage(&msg);
717             DispatchMessage(&msg);
718          } else {
719             return 1;   // WM_QUIT
720          }
721 
722          // only force a draw for certain messages...
723          // if I don't do this, we peg at 50% for some reason... must
724          // be a bug somewhere, because we peg at 100% when rendering...
725          // very weird... looks like NVIDIA is pumping some messages
726          // through our pipeline? well, ok, I guess if we can get
727          // non-user-generated messages we have to do this
728          if (!stbwingraph_force_update) {
729             switch (msg.message) {
730                case WM_MOUSEMOVE:
731                case WM_NCMOUSEMOVE:
732                   break;
733                case WM_CHAR:
734                case WM_KEYDOWN:
735                case WM_KEYUP:
736                case WM_LBUTTONDOWN:
737                case WM_MBUTTONDOWN:
738                case WM_RBUTTONDOWN:
739                case WM_LBUTTONUP:
740                case WM_MBUTTONUP:
741                case WM_RBUTTONUP:
742                case WM_TIMER:
743                case WM_SIZE:
744                case WM_ACTIVATE:
745                   needs_drawing = TRUE;
746                   break;
747             }
748          } else
749             needs_drawing = TRUE;
750       }
751 
752       // if another message, process that first
753       // @TODO: i don't think this is working, because I can't key ahead
754       // in the SVT demo app
755       if (PeekMessage(&msg, NULL, 0,0, PM_NOREMOVE))
756          continue;
757 
758       // and now call update
759       if (needs_drawing || is_animating) {
760          int real=1, in_client=1;
761          if (stbwingraph_primary_window) {
762             stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(stbwingraph_primary_window, GWL_USERDATA);
763             if (z && !z->active) {
764                real = 0;
765             }
766             if (z)
767                in_client = z->in_client;
768          }
769 
770          if (stbwingraph_primary_window)
771             stbwingraph_SetGLWindow(stbwingraph_primary_window);
772          n = func(stbwingraph_GetTimestep(mintime), real, in_client);
773          if (n == STBWINGRAPH_update_exit)
774             return 0; // update_quit
775 
776          is_animating = (n != STBWINGRAPH_update_pause);
777 
778          needs_drawing = FALSE;
779       }
780    }
781 }
782 
stbwingraph_SwapBuffers(void * win)783 void stbwingraph_SwapBuffers(void *win)
784 {
785    stbwingraph__window *z;
786    if (win == NULL) win = stbwingraph_primary_window;
787    z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA);
788    if (z && z->dc)
789       SwapBuffers(z->dc);
790 }
791 #endif
792 
793 #ifdef STB_WINMAIN
794 void stbwingraph_main(void);
795 
796 char *stb_wingraph_commandline;
797 
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)798 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
799 {
800    {
801       char buffer[1024];
802       // add spaces to either side of the string
803       buffer[0] = ' ';
804       strcpy(buffer+1, lpCmdLine);
805       strcat(buffer, " ");
806       if (strstr(buffer, " -reset ")) {
807          ChangeDisplaySettings(NULL, 0);
808          exit(0);
809       }
810       if (strstr(buffer, " -window ") || strstr(buffer, " -windowed "))
811          stbwingraph_request_windowed = TRUE;
812       else if (strstr(buffer, " -full ") || strstr(buffer, " -fullscreen "))
813          stbwingraph_request_fullscreen = TRUE;
814    }
815    stb_wingraph_commandline = lpCmdLine;
816 
817    stbwingraph_DefineClass(hInstance, "appicon");
818    stbwingraph_main();
819 
820    return 0;
821 }
822 #endif
823 
824 #undef STB_EXTERN
825 #ifdef STB_WINGRAPH_DISABLE_DEFINE_AT_END
826 #undef STB_DEFINE
827 #endif
828 
829 #endif // INCLUDE_STB_WINGRAPH_H
830