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.

291 lines
10 KiB

  1. /*
  2. Copyright 2005-2014 Intel Corporation. All Rights Reserved.
  3. This file is part of Threading Building Blocks.
  4. Threading Building Blocks is free software; you can redistribute it
  5. and/or modify it under the terms of the GNU General Public License
  6. version 2 as published by the Free Software Foundation.
  7. Threading Building Blocks is distributed in the hope that it will be
  8. useful, but WITHOUT ANY WARRANTY; without even the implied warranty
  9. of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with Threading Building Blocks; if not, write to the Free Software
  13. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  14. As a special exception, you may use this file as part of a free software
  15. library without restriction. Specifically, if other files instantiate
  16. templates or use macros or inline functions from this file, or you compile
  17. this file and link it with other files to produce an executable, this
  18. file does not by itself cause the resulting executable to be covered by
  19. the GNU General Public License. This exception does not however
  20. invalidate any other reasons why the executable file might be covered by
  21. the GNU General Public License.
  22. */
  23. /////// Common internal implementation of Windows-specific stuff //////////////
  24. /////// Must be the first included header //////////////
  25. #ifndef __WINVIDEO_H__
  26. #define __WINVIDEO_H__
  27. #ifndef _CRT_SECURE_NO_DEPRECATE
  28. #define _CRT_SECURE_NO_DEPRECATE
  29. #endif
  30. // Check that the target Windows version has all API calls requried.
  31. #ifndef _WIN32_WINNT
  32. # define _WIN32_WINNT 0x0400
  33. #endif
  34. #if _WIN32_WINNT<0x0400
  35. # define YIELD_TO_THREAD() Sleep(0)
  36. #else
  37. # define YIELD_TO_THREAD() SwitchToThread()
  38. #endif
  39. #include "video.h"
  40. #include <fcntl.h>
  41. #include <io.h>
  42. #include <iostream>
  43. #include <fstream>
  44. #pragma comment(lib, "gdi32.lib")
  45. #pragma comment(lib, "user32.lib")
  46. // maximum mumber of lines the output console should have
  47. static const WORD MAX_CONSOLE_LINES = 500;
  48. const COLORREF RGBKEY = RGB(8, 8, 16); // at least 8 for 16-bit palette
  49. HWND g_hAppWnd; // The program's window handle
  50. HANDLE g_handles[2] = {0,0};// thread and wake up event
  51. unsigned int * g_pImg = 0; // drawing memory
  52. int g_sizex, g_sizey;
  53. static video * g_video = 0;
  54. WNDPROC g_pUserProc = 0;
  55. HINSTANCE video::win_hInstance = 0;
  56. int video::win_iCmdShow = 0;
  57. static WNDCLASSEX * gWndClass = 0;
  58. static HACCEL hAccelTable = 0;
  59. static DWORD g_msec = 0;
  60. static int g_fps = 0, g_updates = 0, g_skips = 0;
  61. bool DisplayError(LPSTR lpstrErr, HRESULT hres = 0); // always returns false
  62. LRESULT CALLBACK InternalWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
  63. //! Create window
  64. bool WinInit(HINSTANCE hInstance, int nCmdShow, WNDCLASSEX *uwc, const char *title, bool fixedsize)
  65. {
  66. WNDCLASSEX wndclass; // Our app's windows class
  67. if(uwc) {
  68. memcpy(&wndclass, uwc, sizeof(wndclass));
  69. g_pUserProc = uwc->lpfnWndProc;
  70. } else {
  71. memset(&wndclass, 0, sizeof(wndclass));
  72. wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
  73. wndclass.lpszClassName = title;
  74. }
  75. wndclass.cbSize = sizeof(wndclass);
  76. wndclass.hInstance = hInstance;
  77. wndclass.lpfnWndProc = InternalWndProc;
  78. wndclass.style |= CS_HREDRAW | CS_VREDRAW;
  79. wndclass.hbrBackground = CreateSolidBrush(RGBKEY);
  80. if( !RegisterClassExA(&wndclass) ) return false;
  81. int xaddend = GetSystemMetrics(fixedsize?SM_CXFIXEDFRAME:SM_CXFRAME)*2;
  82. int yaddend = GetSystemMetrics(fixedsize?SM_CYFIXEDFRAME:SM_CYFRAME)*2 + GetSystemMetrics(SM_CYCAPTION);
  83. if(wndclass.lpszMenuName) yaddend += GetSystemMetrics(SM_CYMENU);
  84. // Setup the new window's physical parameters - and tell Windows to create it
  85. g_hAppWnd = CreateWindowA(wndclass.lpszClassName, // Window class name
  86. title, // Window caption
  87. !fixedsize ? WS_OVERLAPPEDWINDOW : // Window style
  88. WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX,
  89. CW_USEDEFAULT, // Initial x pos: use default placement
  90. 0, // Initial y pos: not used here
  91. g_sizex+xaddend,// Initial x size
  92. g_sizey+yaddend,// Initial y size
  93. NULL, // parent window handle
  94. NULL, // window menu handle
  95. hInstance, // program instance handle
  96. NULL); // Creation parameters
  97. return g_hAppWnd != NULL;
  98. }
  99. //! create console window with redirection
  100. static bool RedirectIOToConsole(void)
  101. {
  102. int hConHandle; size_t lStdHandle;
  103. CONSOLE_SCREEN_BUFFER_INFO coninfo;
  104. FILE *fp;
  105. // allocate a console for this app
  106. AllocConsole();
  107. // set the screen buffer to be big enough to let us scroll text
  108. GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
  109. coninfo.dwSize.Y = MAX_CONSOLE_LINES;
  110. SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
  111. // redirect unbuffered STDOUT to the console
  112. lStdHandle = (size_t)GetStdHandle(STD_OUTPUT_HANDLE);
  113. hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
  114. if(hConHandle <= 0) return false;
  115. fp = _fdopen( hConHandle, "w" );
  116. *stdout = *fp;
  117. setvbuf( stdout, NULL, _IONBF, 0 );
  118. // redirect unbuffered STDERR to the console
  119. lStdHandle = (size_t)GetStdHandle(STD_ERROR_HANDLE);
  120. hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
  121. if(hConHandle > 0) {
  122. fp = _fdopen( hConHandle, "w" );
  123. *stderr = *fp;
  124. setvbuf( stderr, NULL, _IONBF, 0 );
  125. }
  126. // redirect unbuffered STDIN to the console
  127. lStdHandle = (size_t)GetStdHandle(STD_INPUT_HANDLE);
  128. hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
  129. if(hConHandle > 0) {
  130. fp = _fdopen( hConHandle, "r" );
  131. *stdin = *fp;
  132. setvbuf( stdin, NULL, _IONBF, 0 );
  133. }
  134. // make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
  135. // point to console as well
  136. std::ios::sync_with_stdio();
  137. return true;
  138. }
  139. video::video()
  140. : red_mask(0xff0000), red_shift(16), green_mask(0xff00),
  141. green_shift(8), blue_mask(0xff), blue_shift(0), depth(24)
  142. {
  143. assert(g_video == 0);
  144. g_video = this; title = "Video"; running = threaded = calc_fps = false; updating = true;
  145. }
  146. //! optionally call it just before init() to set own
  147. void video::win_set_class(WNDCLASSEX &wcex)
  148. {
  149. gWndClass = &wcex;
  150. }
  151. void video::win_load_accelerators(int idc)
  152. {
  153. hAccelTable = LoadAccelerators(win_hInstance, MAKEINTRESOURCE(idc));
  154. }
  155. bool video::init_console()
  156. {
  157. if(RedirectIOToConsole()) {
  158. if(!g_pImg && g_sizex && g_sizey)
  159. g_pImg = new unsigned int[g_sizex * g_sizey];
  160. if(g_pImg) running = true;
  161. return true;
  162. }
  163. return false;
  164. }
  165. video::~video()
  166. {
  167. if(g_video) terminate();
  168. }
  169. DWORD WINAPI thread_video(LPVOID lpParameter)
  170. {
  171. video *v = (video*)lpParameter;
  172. v->on_process();
  173. return 0;
  174. }
  175. static bool loop_once(video *v)
  176. {
  177. // screen update notify
  178. if(int updates = g_updates) {
  179. g_updates = 0;
  180. if(g_video->updating) { g_skips += updates-1; g_fps++; }
  181. else g_skips += updates;
  182. UpdateWindow(g_hAppWnd);
  183. }
  184. // update fps
  185. DWORD msec = GetTickCount();
  186. if(v->calc_fps && msec >= g_msec+1000) {
  187. double sec = (msec - g_msec)/1000.0;
  188. char buffer[256], n = _snprintf(buffer, 128, "%s: %d fps", v->title, int(double(g_fps + g_skips)/sec));
  189. if(g_skips) _snprintf(buffer+n, 128, " - %d skipped = %d updates", int(g_skips/sec), int(g_fps/sec));
  190. SetWindowTextA(g_hAppWnd, buffer);
  191. g_msec = msec; g_skips = g_fps = 0;
  192. }
  193. // event processing, including painting
  194. MSG msg;
  195. if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){
  196. if( msg.message == WM_QUIT ) { v->running = false; return false; }
  197. if( !hAccelTable || !TranslateAccelerator(msg.hwnd, hAccelTable, &msg) ){
  198. TranslateMessage(&msg);
  199. DispatchMessage(&msg);
  200. }
  201. return true; // try again
  202. }
  203. return false;
  204. }
  205. //! Do standard event loop
  206. void video::main_loop()
  207. {
  208. // let Windows draw and unroll the window
  209. InvalidateRect(g_hAppWnd, 0, false);
  210. g_msec = GetTickCount(); // let's stay for 0,5 sec
  211. while(g_msec + 500 > GetTickCount()) { loop_once(this); Sleep(1); }
  212. g_msec = GetTickCount();
  213. // now, start main process
  214. if(threaded) {
  215. g_handles[0] = CreateThread (
  216. NULL, // LPSECURITY_ATTRIBUTES security_attrs
  217. 0, // SIZE_T stacksize
  218. (LPTHREAD_START_ROUTINE) thread_video,
  219. this, // argument
  220. 0, 0);
  221. if(!g_handles[0]) { DisplayError("Can't create thread"); return; }
  222. else // harmless race is possible here
  223. g_handles[1] = CreateEvent(NULL, false, false, NULL);
  224. while(running) {
  225. while(loop_once(this));
  226. YIELD_TO_THREAD(); // give time for processing when running on single CPU
  227. DWORD r = MsgWaitForMultipleObjects(2, g_handles, false, INFINITE, QS_ALLINPUT^QS_MOUSEMOVE);
  228. if(r == WAIT_OBJECT_0) break; // thread terminated
  229. }
  230. running = false;
  231. if(WaitForSingleObject(g_handles[0], 3000) == WAIT_TIMEOUT){
  232. // there was not enough time for graceful shutdown, killing the example with code 1.
  233. exit(1);
  234. }
  235. if(g_handles[0]) CloseHandle(g_handles[0]);
  236. if(g_handles[1]) CloseHandle(g_handles[1]);
  237. g_handles[0] = g_handles[1] = 0;
  238. }
  239. else on_process();
  240. }
  241. //! Refresh screen picture
  242. bool video::next_frame()
  243. {
  244. if(!running) return false;
  245. g_updates++; // Fast but inaccurate counter. The data race here is benign.
  246. if(!threaded) while(loop_once(this));
  247. else if(g_handles[1]) {
  248. SetEvent(g_handles[1]);
  249. YIELD_TO_THREAD();
  250. }
  251. return true;
  252. }
  253. //! Change window title
  254. void video::show_title()
  255. {
  256. if(g_hAppWnd)
  257. SetWindowTextA(g_hAppWnd, title);
  258. }
  259. #endif //__WINVIDEO_H__