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