1 /* 2 Copyright (c) 2005-2021 Intel Corporation 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 /////// Common internal implementation of Windows-specific stuff ////////////// 18 /////// Must be the first included header ////////////// 19 20 #ifndef __WINVIDEO_H__ 21 #define __WINVIDEO_H__ 22 23 #ifndef _CRT_SECURE_NO_DEPRECATE 24 #define _CRT_SECURE_NO_DEPRECATE 25 #endif 26 // Check that the target Windows version has all API calls required. 27 #ifndef _WIN32_WINNT 28 #define _WIN32_WINNT 0x0400 29 #endif 30 #if _WIN32_WINNT < 0x0400 31 #define YIELD_TO_THREAD() Sleep(0) 32 #else 33 #define YIELD_TO_THREAD() SwitchToThread() 34 #endif 35 #include "video.hpp" 36 #include <fcntl.h> 37 #include <io.h> 38 #include <iostream> 39 #include <fstream> 40 41 #pragma comment(lib, "gdi32.lib") 42 #pragma comment(lib, "user32.lib") 43 44 // maximum number of lines the output console should have 45 static const WORD MAX_CONSOLE_LINES = 500; 46 const COLORREF RGBKEY = RGB(8, 8, 16); // at least 8 for 16-bit palette 47 HWND g_hAppWnd; // The program's window handle 48 HANDLE g_handles[2] = { 0, 0 }; // thread and wake up event 49 unsigned int *g_pImg = 0; // drawing memory 50 int g_sizex, g_sizey; 51 static video *g_video = 0; 52 WNDPROC g_pUserProc = 0; 53 HINSTANCE video::win_hInstance = 0; 54 int video::win_iCmdShow = 0; 55 static WNDCLASSEX *gWndClass = 0; 56 static HACCEL hAccelTable = 0; 57 static DWORD g_msec = 0; 58 static int g_fps = 0, g_updates = 0, g_skips = 0; 59 60 bool DisplayError(LPSTR lpstrErr, HRESULT hres = 0); // always returns false 61 LRESULT CALLBACK InternalWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam); 62 63 //! Create window 64 bool WinInit(HINSTANCE hInstance, 65 int nCmdShow, 66 WNDCLASSEX *uwc, 67 const char *title, 68 bool fixedsize) { 69 WNDCLASSEX wndclass; // Our app's windows class 70 if (uwc) { 71 memcpy(&wndclass, uwc, sizeof(wndclass)); 72 g_pUserProc = uwc->lpfnWndProc; 73 } 74 else { 75 memset(&wndclass, 0, sizeof(wndclass)); 76 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); 77 wndclass.lpszClassName = title; 78 } 79 wndclass.cbSize = sizeof(wndclass); 80 wndclass.hInstance = hInstance; 81 wndclass.lpfnWndProc = InternalWndProc; 82 wndclass.style |= CS_HREDRAW | CS_VREDRAW; 83 wndclass.hbrBackground = CreateSolidBrush(RGBKEY); 84 85 if (!RegisterClassExA(&wndclass)) 86 return false; 87 int xaddend = GetSystemMetrics(fixedsize ? SM_CXFIXEDFRAME : SM_CXFRAME) * 2; 88 int yaddend = GetSystemMetrics(fixedsize ? SM_CYFIXEDFRAME : SM_CYFRAME) * 2 + 89 GetSystemMetrics(SM_CYCAPTION); 90 if (wndclass.lpszMenuName) 91 yaddend += GetSystemMetrics(SM_CYMENU); 92 93 // Setup the new window's physical parameters - and tell Windows to create it 94 g_hAppWnd = CreateWindowA(wndclass.lpszClassName, // Window class name 95 title, // Window caption 96 !fixedsize ? WS_OVERLAPPEDWINDOW : // Window style 97 WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, 98 CW_USEDEFAULT, // Initial x pos: use default placement 99 0, // Initial y pos: not used here 100 g_sizex + xaddend, // Initial x size 101 g_sizey + yaddend, // Initial y size 102 NULL, // parent window handle 103 NULL, // window menu handle 104 hInstance, // program instance handle 105 NULL); // Creation parameters 106 return g_hAppWnd != NULL; 107 } 108 109 //! create console window with redirection 110 static bool RedirectIOToConsole(void) { 111 int hConHandle; 112 size_t lStdHandle; 113 CONSOLE_SCREEN_BUFFER_INFO coninfo; 114 FILE *fp; 115 // allocate a console for this app 116 AllocConsole(); 117 118 // set the screen buffer to be big enough to let us scroll text 119 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); 120 coninfo.dwSize.Y = MAX_CONSOLE_LINES; 121 SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); 122 123 // redirect unbuffered STDOUT to the console 124 lStdHandle = (size_t)GetStdHandle(STD_OUTPUT_HANDLE); 125 hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); 126 if (hConHandle <= 0) 127 return false; 128 fp = _fdopen(hConHandle, "w"); 129 *stdout = *fp; 130 setvbuf(stdout, NULL, _IONBF, 0); 131 132 // redirect unbuffered STDERR to the console 133 lStdHandle = (size_t)GetStdHandle(STD_ERROR_HANDLE); 134 hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); 135 if (hConHandle > 0) { 136 fp = _fdopen(hConHandle, "w"); 137 *stderr = *fp; 138 setvbuf(stderr, NULL, _IONBF, 0); 139 } 140 141 // redirect unbuffered STDIN to the console 142 lStdHandle = (size_t)GetStdHandle(STD_INPUT_HANDLE); 143 hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); 144 if (hConHandle > 0) { 145 fp = _fdopen(hConHandle, "r"); 146 *stdin = *fp; 147 setvbuf(stdin, NULL, _IONBF, 0); 148 } 149 150 // make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog 151 // point to console as well 152 std::ios::sync_with_stdio(); 153 return true; 154 } 155 156 video::video() 157 : depth(24), 158 red_shift(16), 159 green_shift(8), 160 blue_shift(0), 161 red_mask(0xff0000), 162 green_mask(0xff00), 163 blue_mask(0xff) { 164 assert(g_video == 0); 165 g_video = this; 166 title = "Video"; 167 running = threaded = calc_fps = false; 168 updating = true; 169 } 170 171 //! optionally call it just before init() to set own 172 void video::win_set_class(WNDCLASSEX &wcex) { 173 gWndClass = &wcex; 174 } 175 176 void video::win_load_accelerators(int idc) { 177 hAccelTable = LoadAccelerators(win_hInstance, MAKEINTRESOURCE(idc)); 178 } 179 180 bool video::init_console() { 181 if (RedirectIOToConsole()) { 182 if (!g_pImg && g_sizex && g_sizey) 183 g_pImg = new unsigned int[g_sizex * g_sizey]; 184 if (g_pImg) 185 running = true; 186 return true; 187 } 188 return false; 189 } 190 191 video::~video() { 192 if (g_video) 193 terminate(); 194 } 195 196 DWORD WINAPI thread_video(LPVOID lpParameter) { 197 video *v = (video *)lpParameter; 198 v->on_process(); 199 return 0; 200 } 201 202 static bool loop_once(video *v) { 203 // screen update notify 204 if (int updates = g_updates) { 205 g_updates = 0; 206 if (g_video->updating) { 207 g_skips += updates - 1; 208 g_fps++; 209 } 210 else 211 g_skips += updates; 212 UpdateWindow(g_hAppWnd); 213 } 214 // update fps 215 DWORD msec = GetTickCount(); 216 if (v->calc_fps && msec >= g_msec + 1000) { 217 double sec = (msec - g_msec) / 1000.0; 218 char buffer[256], 219 n = _snprintf(buffer, 128, "%s: %d fps", v->title, int(double(g_fps + g_skips) / sec)); 220 if (g_skips) 221 _snprintf(buffer + n, 222 128, 223 " - %d skipped = %d updates", 224 int(g_skips / sec), 225 int(g_fps / sec)); 226 SetWindowTextA(g_hAppWnd, buffer); 227 g_msec = msec; 228 g_skips = g_fps = 0; 229 } 230 // event processing, including painting 231 MSG msg; 232 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { 233 if (msg.message == WM_QUIT) { 234 v->running = false; 235 return false; 236 } 237 if (!hAccelTable || !TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { 238 TranslateMessage(&msg); 239 DispatchMessage(&msg); 240 } 241 return true; // try again 242 } 243 return false; 244 } 245 246 //! Do standard event loop 247 void video::main_loop() { 248 // let Windows draw and unroll the window 249 InvalidateRect(g_hAppWnd, 0, false); 250 g_msec = GetTickCount(); // let's stay for 0,5 sec 251 while (g_msec + 500 > GetTickCount()) { 252 loop_once(this); 253 Sleep(1); 254 } 255 g_msec = GetTickCount(); 256 // now, start main process 257 if (threaded) { 258 g_handles[0] = CreateThread(NULL, // LPSECURITY_ATTRIBUTES security_attrs 259 0, // SIZE_T stacksize 260 (LPTHREAD_START_ROUTINE)thread_video, 261 this, // argument 262 0, 263 0); 264 if (!g_handles[0]) { 265 DisplayError("Can't create thread"); 266 return; 267 } 268 else // harmless race is possible here 269 g_handles[1] = CreateEvent(NULL, false, false, NULL); 270 while (running) { 271 while (loop_once(this)) 272 ; 273 YIELD_TO_THREAD(); // give time for processing when running on single CPU 274 DWORD r = MsgWaitForMultipleObjects( 275 2, g_handles, false, INFINITE, QS_ALLINPUT ^ QS_MOUSEMOVE); 276 if (r == WAIT_OBJECT_0) 277 break; // thread terminated 278 } 279 running = false; 280 if (WaitForSingleObject(g_handles[0], 3000) == WAIT_TIMEOUT) { 281 // there was not enough time for graceful shutdown, killing the example with code 1. 282 exit(1); 283 } 284 if (g_handles[0]) 285 CloseHandle(g_handles[0]); 286 if (g_handles[1]) 287 CloseHandle(g_handles[1]); 288 g_handles[0] = g_handles[1] = 0; 289 } 290 else 291 on_process(); 292 } 293 294 //! Refresh screen picture 295 bool video::next_frame() { 296 if (!running) 297 return false; 298 g_updates++; // Fast but inaccurate counter. The data race here is benign. 299 if (!threaded) 300 while (loop_once(this)) 301 ; 302 else if (g_handles[1]) { 303 SetEvent(g_handles[1]); 304 YIELD_TO_THREAD(); 305 } 306 return true; 307 } 308 309 //! Change window title 310 void video::show_title() { 311 if (g_hAppWnd) 312 SetWindowTextA(g_hAppWnd, title); 313 } 314 315 #endif //__WINVIDEO_H__ 316