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