1 // This is core/vgui/impl/win32/vgui_win32.cxx
2 #include <iostream>
3 #include <cstring>
4 #include "vgui_win32.h"
5
6 #include "vgui/vgui_gl.h" // for glFlush()
7 #ifdef _MSC_VER
8 # include "vcl_msvc_warnings.h"
9 #endif
10 #include <cassert> // for assert
11 #include "vgui_win32_window.h"
12 #include "vgui_win32_dialog_impl.h"
13
14 vgui_win32 * vgui_win32::instance_ = 0;
15
16 // Return a single instance of vgui_win32.
17 vgui_win32 *
instance()18 vgui_win32::instance()
19 {
20 if (instance_ == 0)
21 instance_ = new vgui_win32();
22
23 return instance_;
24 }
25
vgui_win32()26 vgui_win32::vgui_win32()
27 {
28 hInstance_ = GetModuleHandle(NULL);
29 hPrevInstance_ = NULL;
30 szCmdLine_ = NULL;
31 iCmdShow_ = SW_SHOW;
32
33 szAppName_ = NULL;
34
35 windows_to_delete.clear();
36 current_window = NULL;
37 dialogs_to_delete.clear();
38 current_dialog = NULL;
39 }
40
~vgui_win32()41 vgui_win32::~vgui_win32() {}
42
43
44 // Process command line arguments
45 BOOL
ProcessShellCommand(int argc,char ** argv)46 vgui_win32::ProcessShellCommand(int argc, char ** argv)
47 {
48 // We set szAppName_ to be the app name.
49 char *p, *q = argv[0];
50
51 // Skip path
52 while (p = std::strchr(q, '\\'))
53 q = ++p;
54 // remove ".exe" suffix
55 p = std::strstr(q, ".exe");
56 // Now q points to the app name.
57 if (p)
58 szAppName_ = (char *)malloc(sizeof(char) * (p - q + 1));
59 else // .exe is not provided.
60 szAppName_ = (char *)malloc(sizeof(char) * (1 + std::strlen(q)));
61
62 if (szAppName_ == NULL)
63 return FALSE;
64 if (p)
65 *p = 0;
66 std::strcpy(szAppName_, q);
67
68 // Convert argv into szCmdLine_
69 szCmdLine_ = GetCommandLine();
70
71 return TRUE;
72 }
73
74 void
init(int & argc,char ** argv)75 vgui_win32::init(int & argc, char ** argv)
76 {
77 ProcessShellCommand(argc, argv);
78
79 WNDCLASS wndclass;
80 wndclass.style = CS_HREDRAW | CS_VREDRAW;
81 wndclass.lpfnWndProc = globalWndProc;
82 wndclass.cbClsExtra = 0;
83 wndclass.cbWndExtra = 0;
84 wndclass.hInstance = hInstance_;
85 wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
86 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
87 wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
88 wndclass.lpszMenuName = szAppName_;
89 wndclass.lpszClassName = szAppName_;
90
91 if (!RegisterClass(&wndclass))
92 {
93 std::cerr << "Fail to register window class for main window.\n";
94 assert(false); // quit ugly
95 }
96
97 // Register a window class for dialog box.
98 wndclass.style = CS_HREDRAW | CS_VREDRAW;
99 wndclass.lpfnWndProc = globalDialogProc;
100 wndclass.cbClsExtra = 0;
101 wndclass.cbWndExtra = 0;
102 wndclass.hInstance = hInstance_;
103 wndclass.hIcon = LoadIcon(hInstance_, IDI_APPLICATION);
104 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
105 wndclass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
106 wndclass.lpszMenuName = NULL;
107 wndclass.lpszClassName = TEXT("vgui_win32_dialog");
108
109 if (!RegisterClass(&wndclass))
110 {
111 MessageBox(NULL, TEXT("Fail to register window class for dialog box!"), NULL, MB_ICONERROR);
112 assert(false); // quit ugly
113 }
114
115 // Register a window class for inline tableau control
116 wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
117 wndclass.lpfnWndProc = globalTableauProc;
118 wndclass.cbClsExtra = 0;
119 wndclass.cbWndExtra = 0;
120 wndclass.hInstance = hInstance_;
121 wndclass.hIcon = LoadIcon(hInstance_, IDI_APPLICATION);
122 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
123 wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
124 wndclass.lpszMenuName = NULL;
125 wndclass.lpszClassName = TEXT("vgui_win32_inline_tab");
126
127 if (!RegisterClass(&wndclass))
128 {
129 MessageBox(NULL, TEXT("Fail to register window class for inline tableau control!"), NULL, MB_ICONERROR);
130 assert(false); // quit ugly
131 }
132 }
133
134 void
uninit()135 vgui_win32::uninit()
136 {
137 for (unsigned i = 0; i < windows_to_delete.size(); i++)
138 delete windows_to_delete[i];
139
140 // dialogs are already freed at the end of scope of caller function.
141 }
142
143 vgui_window *
produce_window(int width,int height,vgui_menu const & menubar,char const * title)144 vgui_win32::produce_window(int width, int height, vgui_menu const & menubar, char const * title)
145 {
146 vgui_window * a_window = new vgui_win32_window(hInstance_, szAppName_, width, height, menubar, title);
147 windows_to_delete.push_back(a_window);
148 current_window = a_window;
149 return a_window;
150 }
151
152 vgui_window *
produce_window(int width,int height,char const * title)153 vgui_win32::produce_window(int width, int height, char const * title)
154 {
155 vgui_window * a_window = new vgui_win32_window(hInstance_, szAppName_, width, height, title);
156 windows_to_delete.push_back(a_window);
157 current_window = a_window;
158 return a_window;
159 }
160
161 vgui_dialog_impl *
produce_dialog(char const * name)162 vgui_win32::produce_dialog(char const * name)
163 {
164 vgui_window * win = get_current_window();
165
166 current_dialog = new vgui_win32_dialog_impl(name, win ? ((vgui_win32_window *)win)->getWindowHandle() : NULL);
167 dialogs_to_delete.push_back(current_dialog);
168 return current_dialog;
169 }
170
171 vgui_dialog_extensions_impl *
produce_dialog_extension(char const * name)172 vgui_win32::produce_dialog_extension(char const * name)
173 {
174 // return new vgui_win32_dialog_extension_impl(name);
175 return 0;
176 }
177
178 // Run the event loop. Windows uses messages
179 void
run()180 vgui_win32::run()
181 {
182 MSG msg;
183
184 while (GetMessage(&msg, NULL, 0, 0))
185 {
186 vgui_win32_window * pwin = (vgui_win32_window *)get_current_window();
187 if (pwin)
188 {
189 HWND hwnd = pwin->getWindowHandle();
190 HACCEL hAccel = pwin->getAccelHandle();
191 if (!(hAccel && TranslateAccelerator(hwnd, hAccel, &msg)))
192 {
193 TranslateMessage(&msg);
194 DispatchMessage(&msg);
195 }
196 }
197 else
198 {
199 TranslateMessage(&msg);
200 DispatchMessage(&msg);
201 }
202 }
203 }
204
205 // TODO: This function is not called yet.
206 void
run_one_event()207 vgui_win32::run_one_event()
208 {
209 if (!PumpMessage())
210 PostQuitMessage(0);
211 }
212
213 // TODO: This function is not called yet.
214 void
run_till_idle()215 vgui_win32::run_till_idle()
216 {
217 MSG msg;
218
219 while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
220 {
221 if (!PumpMessage())
222 {
223 PostQuitMessage(0);
224 break;
225 }
226 }
227 }
228
229 // TODO: This function is not called yet.
230 // Remove all events from the event queue. Copy the code from that of
231 // counterparts such as mfc, gkt2.
232 void
flush()233 vgui_win32::flush()
234 {
235 glFlush();
236 run_till_idle();
237 }
238
239 // TODO: This function is not called yet.
240 // Add event to the event queue.
241 void
add_event(vgui_event const & e)242 vgui_win32::add_event(vgui_event const & e)
243 {
244 // PostMessage(); // TODO
245 }
246
247 // Quit application
248 void
quit()249 vgui_win32::quit()
250 {
251 PostQuitMessage(0);
252 }
253
254 // TODO: This function is not called yet.
255 // Pump a message from the thread's message queue and process it.
256 BOOL
PumpMessage()257 vgui_win32::PumpMessage()
258 {
259 MSG msg;
260
261 if (!GetMessage(&msg, NULL, 0, 0))
262 return FALSE;
263
264 TranslateMessage(&msg);
265 DispatchMessage(&msg);
266
267 return TRUE;
268 }
269
270 // Find the window whose handle is hwnd.
271 inline int
find_window(HWND hwnd)272 vgui_win32::find_window(HWND hwnd)
273 {
274 int i;
275 std::vector<vgui_window *>::const_iterator it;
276 for (i = 0, it = windows_to_delete.begin(); it != windows_to_delete.end(); it++, i++)
277 {
278 HWND the_hwnd = ((vgui_win32_window *)(*it))->getWindowHandle();
279 if (the_hwnd == hwnd)
280 return i;
281 }
282 // Not found
283 return -1;
284 }
285
286 inline void
dump_window_stack()287 vgui_win32::dump_window_stack()
288 {
289 std::cout << "z-top of window stack: ";
290
291 std::vector<vgui_window *>::const_iterator it;
292 for (it = windows_to_delete.begin(); it != windows_to_delete.end(); it++)
293 std::cout << ((vgui_win32_window *)(*it))->getWindowHandle() << ',';
294 std::cout << std::endl;
295 }
296
297 // Set the current window as the one whose handle is "hwnd".
298 // In other words, raise the window hwnd to the top of z-order stack
299 // of windows.
300 void
set_current_window(HWND hwnd)301 vgui_win32::set_current_window(HWND hwnd)
302 {
303 int n = find_window(hwnd);
304 if (n == -1 || windows_to_delete[n] == current_window)
305 return; // do nothing if hwnd is already top-most, or not found.
306
307 current_window = windows_to_delete[n];
308 windows_to_delete.erase(windows_to_delete.begin() + n);
309 windows_to_delete.push_back(current_window);
310 // dump_window_stack();
311 }
312
313
314 // Remove the current window from z-order stack of windows.
315 void
remove_current_window()316 vgui_win32::remove_current_window()
317 {
318 windows_to_delete.pop_back();
319 delete current_window;
320 current_window = windows_to_delete.empty() ? NULL : windows_to_delete.back();
321 // dump_window_stack();
322 }
323
324 void
remove_current_dialog()325 vgui_win32::remove_current_dialog()
326 {
327 dialogs_to_delete.pop_back();
328 current_dialog = dialogs_to_delete.empty() ? NULL : dialogs_to_delete.back();
329 }
330
331 LRESULT CALLBACK
globalWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)332 globalWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
333 {
334 LRESULT lResult = 0;
335 vgui_win32_window * pwin;
336
337 // Some messages should be handled here, not in vgui_window and vgui_adaptor
338
339 vgui_win32::instance()->set_current_window(hwnd);
340
341 if (message == WM_DESTROY)
342 {
343 // When multiple vgui_window objects are created, a WM_DESTROY message
344 // is sent when one of the vgui_window is closed.
345 // In this case, two operations have to be done. First, the current
346 // window (along with device context and OpenGL rendering context)
347 // should be switched. Second, the WM_DESTROY message should be
348 // blocked unless no vgui_window object exists. Otherwise, the
349 // application will quit since function PostQuitMessage() is called
350 // (ie. WM_QUIT is sent) in response to WM_DESTROY message.
351
352 vgui_win32::instance()->remove_current_window();
353
354 // Call post_redraw to switch device context and GL rendering context.
355 pwin = (vgui_win32_window *)vgui_win32::instance()->get_current_window();
356 if (pwin)
357 pwin->get_adaptor()->post_redraw();
358
359 // block WM_DESTROY message
360 return 1;
361 }
362
363 pwin = (vgui_win32_window *)vgui_win32::instance()->get_current_window();
364 if (pwin)
365 lResult = pwin->WndProc(hwnd, message, wParam, lParam);
366
367 return lResult ? lResult : DefWindowProc(hwnd, message, wParam, lParam);
368 }
369
370 LRESULT CALLBACK
globalDialogProc(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam)371 globalDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
372 {
373 LRESULT lResult = 0;
374 vgui_win32_window * pwin;
375 vgui_win32_dialog_impl * dlg;
376
377
378 dlg = (vgui_win32_dialog_impl *)vgui_win32::instance()->get_current_dialog();
379 if (dlg)
380 lResult = dlg->DialogProc(hDlg, message, wParam, lParam);
381
382 if (message == WM_DESTROY)
383 {
384 // Call post_redraw to switch device context and GL rendering context.
385 pwin = (vgui_win32_window *)vgui_win32::instance()->get_current_window();
386 pwin->get_adaptor()->post_redraw();
387
388 // Reset pointer to current dialog box.
389 (vgui_win32::instance())->remove_current_dialog();
390 }
391
392 return lResult ? lResult : DefWindowProc(hDlg, message, wParam, lParam);
393 }
394
395 LRESULT CALLBACK
globalTableauProc(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam)396 globalTableauProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
397 {
398 vgui_win32_dialog_impl * dlg;
399
400 // Get the dialog box the inline tableau belongs.
401 dlg = (vgui_win32_dialog_impl *)vgui_win32::instance()->get_current_dialog();
402
403 if (dlg && dlg->get_inline_tableau_size() > 0)
404 dlg->get_current_tab()->OnCmdMsg(message, wParam, lParam);
405
406 return DefWindowProc(hDlg, message, wParam, lParam);
407 }
408