Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

829 lines
24 KiB

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