xref: /oneTBB/examples/common/gui/winvideo.hpp (revision c21e688a)
1 /*
2     Copyright (c) 2005-2022 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
WinInit(HINSTANCE hInstance,int nCmdShow,WNDCLASSEX * uwc,const char * title,bool fixedsize)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(nullptr, 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                               nullptr, // parent window handle
103                               nullptr, // window menu handle
104                               hInstance, // program instance handle
105                               nullptr); // Creation parameters
106     return g_hAppWnd != nullptr;
107 }
108 
109 //! create console window with redirection
RedirectIOToConsole(void)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, nullptr, _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, nullptr, _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, nullptr, _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 
video()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
win_set_class(WNDCLASSEX & wcex)172 void video::win_set_class(WNDCLASSEX &wcex) {
173     gWndClass = &wcex;
174 }
175 
win_load_accelerators(int idc)176 void video::win_load_accelerators(int idc) {
177     hAccelTable = LoadAccelerators(win_hInstance, MAKEINTRESOURCE(idc));
178 }
179 
init_console()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 
~video()191 video::~video() {
192     if (g_video)
193         terminate();
194 }
195 
thread_video(LPVOID lpParameter)196 DWORD WINAPI thread_video(LPVOID lpParameter) {
197     video *v = (video *)lpParameter;
198     v->on_process();
199     return 0;
200 }
201 
loop_once(video * v)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, nullptr, 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
main_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(nullptr, // 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(nullptr, false, false, nullptr);
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
next_frame()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
show_title()310 void video::show_title() {
311     if (g_hAppWnd)
312         SetWindowTextA(g_hAppWnd, title);
313 }
314 
315 #endif //__WINVIDEO_H__
316