1 | /************************************************************************* |
---|
2 | * * |
---|
3 | * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * |
---|
4 | * All rights reserved. Email: russ@q12.org Web: www.q12.org * |
---|
5 | * * |
---|
6 | * This library is free software; you can redistribute it and/or * |
---|
7 | * modify it under the terms of EITHER: * |
---|
8 | * (1) The GNU Lesser General Public License as published by the Free * |
---|
9 | * Software Foundation; either version 2.1 of the License, or (at * |
---|
10 | * your option) any later version. The text of the GNU Lesser * |
---|
11 | * General Public License is included with this library in the * |
---|
12 | * file LICENSE.TXT. * |
---|
13 | * (2) The BSD-style license that is included with this library in * |
---|
14 | * the file LICENSE-BSD.TXT. * |
---|
15 | * * |
---|
16 | * This library is distributed in the hope that it will be useful, * |
---|
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * |
---|
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files * |
---|
19 | * LICENSE.TXT and LICENSE-BSD.TXT for more details. * |
---|
20 | * * |
---|
21 | *************************************************************************/ |
---|
22 | |
---|
23 | #if defined(WIN32) || defined(__CYGWIN__)// this prevents warnings when dependencies built |
---|
24 | #include <windows.h> |
---|
25 | #endif |
---|
26 | #include <ode/config.h> |
---|
27 | #include <GL/gl.h> |
---|
28 | |
---|
29 | #include "resource.h" |
---|
30 | #include "internal.h" |
---|
31 | |
---|
32 | //*************************************************************************** |
---|
33 | // application globals |
---|
34 | |
---|
35 | static HINSTANCE ghInstance = 0; |
---|
36 | static int gnCmdShow = 0; |
---|
37 | static HACCEL accelerators = 0; |
---|
38 | static HWND main_window = 0; |
---|
39 | |
---|
40 | //*************************************************************************** |
---|
41 | // error and message handling |
---|
42 | |
---|
43 | static void errorBox (char *title, char *msg, va_list ap) |
---|
44 | { |
---|
45 | char s[1000]; |
---|
46 | vsprintf (s,msg,ap); |
---|
47 | MessageBox (0,s,title,MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION); |
---|
48 | } |
---|
49 | |
---|
50 | |
---|
51 | static void dsWarning (char *msg, ...) |
---|
52 | { |
---|
53 | va_list ap; |
---|
54 | va_start (ap,msg); |
---|
55 | errorBox ("Warning",msg,ap); |
---|
56 | } |
---|
57 | |
---|
58 | |
---|
59 | extern "C" void dsError (char *msg, ...) |
---|
60 | { |
---|
61 | va_list ap; |
---|
62 | va_start (ap,msg); |
---|
63 | errorBox ("Error",msg,ap); |
---|
64 | exit (1); |
---|
65 | } |
---|
66 | |
---|
67 | |
---|
68 | extern "C" void dsDebug (char *msg, ...) |
---|
69 | { |
---|
70 | va_list ap; |
---|
71 | va_start (ap,msg); |
---|
72 | errorBox ("INTERNAL ERROR",msg,ap); |
---|
73 | // *((char *)0) = 0; ... commit SEGVicide ? |
---|
74 | abort(); |
---|
75 | exit (1); // should never get here, but just in case... |
---|
76 | } |
---|
77 | |
---|
78 | |
---|
79 | extern "C" void dsPrint (char *msg, ...) |
---|
80 | { |
---|
81 | va_list ap; |
---|
82 | va_start (ap,msg); |
---|
83 | vprintf (msg,ap); |
---|
84 | } |
---|
85 | |
---|
86 | //*************************************************************************** |
---|
87 | // rendering thread |
---|
88 | |
---|
89 | // globals used to communicate with rendering thread |
---|
90 | |
---|
91 | static volatile int renderer_run = 1; |
---|
92 | static volatile int renderer_pause = 0; // 0=run, 1=pause |
---|
93 | static volatile int renderer_ss = 0; // single step command |
---|
94 | static volatile int renderer_width = 1; |
---|
95 | static volatile int renderer_height = 1; |
---|
96 | static dsFunctions *renderer_fn = 0; |
---|
97 | static volatile HDC renderer_dc = 0; |
---|
98 | static volatile int keybuffer[16]; // fifo ring buffer for keypresses |
---|
99 | static volatile int keybuffer_head = 0; // index of next key to put in (modified by GUI) |
---|
100 | static volatile int keybuffer_tail = 0; // index of next key to take out (modified by renderer) |
---|
101 | |
---|
102 | |
---|
103 | static void setupRendererGlobals() |
---|
104 | { |
---|
105 | renderer_run = 1; |
---|
106 | renderer_pause = 0; |
---|
107 | renderer_ss = 0; |
---|
108 | renderer_width = 1; |
---|
109 | renderer_height = 1; |
---|
110 | renderer_fn = 0; |
---|
111 | renderer_dc = 0; |
---|
112 | keybuffer[16]; |
---|
113 | keybuffer_head = 0; |
---|
114 | keybuffer_tail = 0; |
---|
115 | } |
---|
116 | |
---|
117 | |
---|
118 | static DWORD WINAPI renderingThread (LPVOID lpParam) |
---|
119 | { |
---|
120 | // create openGL context and make it current |
---|
121 | HGLRC glc = wglCreateContext (renderer_dc); |
---|
122 | if (glc==NULL) dsError ("could not create OpenGL context"); |
---|
123 | if (wglMakeCurrent (renderer_dc,glc) != TRUE) |
---|
124 | dsError ("could not make OpenGL context current"); |
---|
125 | |
---|
126 | // test openGL capabilities |
---|
127 | int maxtsize=0; |
---|
128 | glGetIntegerv (GL_MAX_TEXTURE_SIZE,&maxtsize); |
---|
129 | if (maxtsize < 128) dsWarning ("max texture size too small (%dx%d)", |
---|
130 | maxtsize,maxtsize); |
---|
131 | |
---|
132 | dsStartGraphics (renderer_width,renderer_height,renderer_fn); |
---|
133 | if (renderer_fn->start) renderer_fn->start(); |
---|
134 | |
---|
135 | while (renderer_run) { |
---|
136 | // need to make local copy of renderer_ss to help prevent races |
---|
137 | int ss = renderer_ss; |
---|
138 | dsDrawFrame (renderer_width,renderer_height,renderer_fn, |
---|
139 | renderer_pause && !ss); |
---|
140 | if (ss) renderer_ss = 0; |
---|
141 | |
---|
142 | // read keys out of ring buffer and feed them to the command function |
---|
143 | while (keybuffer_head != keybuffer_tail) { |
---|
144 | if (renderer_fn->command) renderer_fn->command (keybuffer[keybuffer_tail]); |
---|
145 | keybuffer_tail = (keybuffer_tail+1) & 15; |
---|
146 | } |
---|
147 | |
---|
148 | // swap buffers |
---|
149 | SwapBuffers (renderer_dc); |
---|
150 | } |
---|
151 | |
---|
152 | if (renderer_fn->stop) renderer_fn->stop(); |
---|
153 | dsStopGraphics(); |
---|
154 | |
---|
155 | // delete openGL context |
---|
156 | wglMakeCurrent (NULL,NULL); |
---|
157 | wglDeleteContext (glc); |
---|
158 | |
---|
159 | return 123; // magic value used to test for thread termination |
---|
160 | } |
---|
161 | |
---|
162 | //*************************************************************************** |
---|
163 | // window handling |
---|
164 | |
---|
165 | // callback function for "about" dialog box |
---|
166 | |
---|
167 | static LRESULT CALLBACK AboutDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, |
---|
168 | LPARAM lParam) |
---|
169 | { |
---|
170 | switch (uMsg) { |
---|
171 | case WM_INITDIALOG: |
---|
172 | return TRUE; |
---|
173 | case WM_COMMAND: |
---|
174 | switch (wParam) { |
---|
175 | case IDOK: |
---|
176 | EndDialog (hDlg, TRUE); |
---|
177 | return TRUE; |
---|
178 | } |
---|
179 | break; |
---|
180 | } |
---|
181 | return FALSE; |
---|
182 | } |
---|
183 | |
---|
184 | |
---|
185 | // callback function for the main window |
---|
186 | |
---|
187 | static LRESULT CALLBACK mainWndProc (HWND hWnd, UINT msg, WPARAM wParam, |
---|
188 | LPARAM lParam) |
---|
189 | { |
---|
190 | static int button=0,lastx=0,lasty=0; |
---|
191 | int ctrl = int(wParam & MK_CONTROL); |
---|
192 | |
---|
193 | switch (msg) { |
---|
194 | case WM_LBUTTONDOWN: |
---|
195 | case WM_MBUTTONDOWN: |
---|
196 | case WM_RBUTTONDOWN: |
---|
197 | if (msg==WM_LBUTTONDOWN) button |= 1; |
---|
198 | else if (msg==WM_MBUTTONDOWN) button |= 2; |
---|
199 | else button |= 4; |
---|
200 | lastx = SHORT(LOWORD(lParam)); |
---|
201 | lasty = SHORT(HIWORD(lParam)); |
---|
202 | SetCapture (hWnd); |
---|
203 | break; |
---|
204 | |
---|
205 | case WM_LBUTTONUP: |
---|
206 | case WM_MBUTTONUP: |
---|
207 | case WM_RBUTTONUP: |
---|
208 | if (msg==WM_LBUTTONUP) button &= ~1; |
---|
209 | else if (msg==WM_MBUTTONUP) button &= ~2; |
---|
210 | else button &= ~4; |
---|
211 | if (button==0) ReleaseCapture(); |
---|
212 | break; |
---|
213 | |
---|
214 | case WM_MOUSEMOVE: { |
---|
215 | int x = SHORT(LOWORD(lParam)); |
---|
216 | int y = SHORT(HIWORD(lParam)); |
---|
217 | if (button) dsMotion (button,x-lastx,y-lasty); |
---|
218 | lastx = x; |
---|
219 | lasty = y; |
---|
220 | break; |
---|
221 | } |
---|
222 | |
---|
223 | case WM_CHAR: { |
---|
224 | if (wParam >= ' ' && wParam <= 126) { |
---|
225 | int nexth = (keybuffer_head+1) & 15; |
---|
226 | if (nexth != keybuffer_tail) { |
---|
227 | keybuffer[keybuffer_head] = int(wParam); |
---|
228 | keybuffer_head = nexth; |
---|
229 | } |
---|
230 | } |
---|
231 | break; |
---|
232 | } |
---|
233 | |
---|
234 | case WM_SIZE: |
---|
235 | // lParam will contain the size of the *client* area! |
---|
236 | renderer_width = LOWORD(lParam); |
---|
237 | renderer_height = HIWORD(lParam); |
---|
238 | break; |
---|
239 | |
---|
240 | case WM_COMMAND: |
---|
241 | switch (wParam & 0xffff) { |
---|
242 | case IDM_ABOUT: |
---|
243 | DialogBox (ghInstance,MAKEINTRESOURCE(IDD_ABOUT),hWnd, |
---|
244 | (DLGPROC) AboutDlgProc); |
---|
245 | break; |
---|
246 | case IDM_PAUSE: { |
---|
247 | renderer_pause ^= 1; |
---|
248 | CheckMenuItem (GetMenu(hWnd),IDM_PAUSE, |
---|
249 | renderer_pause ? MF_CHECKED : MF_UNCHECKED); |
---|
250 | if (renderer_pause) renderer_ss = 0; |
---|
251 | break; |
---|
252 | } |
---|
253 | case IDM_SINGLE_STEP: { |
---|
254 | if (renderer_pause) |
---|
255 | renderer_ss = 1; |
---|
256 | else |
---|
257 | SendMessage( hWnd, WM_COMMAND, IDM_PAUSE, 0 ); |
---|
258 | break; |
---|
259 | } |
---|
260 | case IDM_PERF_MONITOR: { |
---|
261 | dsWarning ("Performance monitor not yet implemented."); |
---|
262 | break; |
---|
263 | } |
---|
264 | case IDM_TEXTURES: { |
---|
265 | static int tex = 1; |
---|
266 | tex ^= 1; |
---|
267 | CheckMenuItem (GetMenu(hWnd),IDM_TEXTURES, |
---|
268 | tex ? MF_CHECKED : MF_UNCHECKED); |
---|
269 | dsSetTextures (tex); |
---|
270 | break; |
---|
271 | } |
---|
272 | case IDM_SHADOWS: { |
---|
273 | static int shadows = 1; |
---|
274 | shadows ^= 1; |
---|
275 | CheckMenuItem (GetMenu(hWnd),IDM_SHADOWS, |
---|
276 | shadows ? MF_CHECKED : MF_UNCHECKED); |
---|
277 | dsSetShadows (shadows); |
---|
278 | break; |
---|
279 | } |
---|
280 | case IDM_SAVE_SETTINGS: { |
---|
281 | dsWarning ("\"Save Settings\" not yet implemented."); |
---|
282 | break; |
---|
283 | } |
---|
284 | case IDM_EXIT: |
---|
285 | PostQuitMessage (0); |
---|
286 | break; |
---|
287 | } |
---|
288 | break; |
---|
289 | |
---|
290 | case WM_CLOSE: |
---|
291 | PostQuitMessage (0); |
---|
292 | break; |
---|
293 | |
---|
294 | default: |
---|
295 | return (DefWindowProc (hWnd, msg, wParam, lParam)); |
---|
296 | } |
---|
297 | |
---|
298 | return 0; |
---|
299 | } |
---|
300 | |
---|
301 | |
---|
302 | // this comes from an MSDN example. believe it or not, this is the recommended |
---|
303 | // way to get the console window handle. |
---|
304 | |
---|
305 | static HWND GetConsoleHwnd() |
---|
306 | { |
---|
307 | // the console window title to a "unique" value, then find the window |
---|
308 | // that has this title. |
---|
309 | char title[1024]; |
---|
310 | wsprintf (title,"DrawStuff:%d/%d",GetTickCount(),GetCurrentProcessId()); |
---|
311 | SetConsoleTitle (title); |
---|
312 | Sleep(40); // ensure window title has been updated |
---|
313 | return FindWindow (NULL,title); |
---|
314 | } |
---|
315 | |
---|
316 | |
---|
317 | static void drawStuffStartup() |
---|
318 | { |
---|
319 | static int startup_called = 0; |
---|
320 | if (startup_called) return; |
---|
321 | startup_called = 1; |
---|
322 | if (!ghInstance) |
---|
323 | ghInstance = GetModuleHandleA (NULL); |
---|
324 | gnCmdShow = SW_SHOWNORMAL; // @@@ fix this later |
---|
325 | |
---|
326 | // redirect standard I/O to a new console (except on cygwin) |
---|
327 | #ifndef __CYGWIN__ |
---|
328 | FreeConsole(); |
---|
329 | if (AllocConsole()==0) dsError ("AllocConsole() failed"); |
---|
330 | if (freopen ("CONIN$","rt",stdin)==0) dsError ("could not open stdin"); |
---|
331 | if (freopen ("CONOUT$","wt",stdout)==0) dsError ("could not open stdout"); |
---|
332 | if (freopen ("CONOUT$","wt",stderr)==0) dsError ("could not open stderr"); |
---|
333 | BringWindowToTop (GetConsoleHwnd()); |
---|
334 | SetConsoleTitle ("DrawStuff Messages"); |
---|
335 | #endif |
---|
336 | |
---|
337 | // register the window class |
---|
338 | WNDCLASS wc; |
---|
339 | wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW; |
---|
340 | wc.lpfnWndProc = mainWndProc; |
---|
341 | wc.cbClsExtra = 0; |
---|
342 | wc.cbWndExtra = 0; |
---|
343 | wc.hInstance = ghInstance; |
---|
344 | wc.hIcon = LoadIcon (NULL,IDI_APPLICATION); |
---|
345 | wc.hCursor = LoadCursor (NULL,IDC_ARROW); |
---|
346 | wc.hbrBackground = (HBRUSH) (COLOR_WINDOW+1); |
---|
347 | wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1); |
---|
348 | wc.lpszClassName = "SimAppClass"; |
---|
349 | if (RegisterClass (&wc)==0) dsError ("could not register window class"); |
---|
350 | |
---|
351 | // load accelerators |
---|
352 | accelerators = LoadAccelerators (ghInstance, |
---|
353 | MAKEINTRESOURCE(IDR_ACCELERATOR1)); |
---|
354 | if (accelerators==NULL) dsError ("could not load accelerators"); |
---|
355 | } |
---|
356 | |
---|
357 | |
---|
358 | void dsPlatformSimLoop (int window_width, int window_height, |
---|
359 | dsFunctions *fn, int initial_pause) |
---|
360 | { |
---|
361 | drawStuffStartup(); |
---|
362 | setupRendererGlobals(); |
---|
363 | renderer_pause = initial_pause; |
---|
364 | |
---|
365 | // create window - but first get window size for desired size of client area. |
---|
366 | // if this adjustment isn't made then the openGL area will be shifted into |
---|
367 | // the nonclient area and determining the frame buffer coordinate from the |
---|
368 | // client area coordinate will be hard. |
---|
369 | RECT winrect; |
---|
370 | winrect.left = 50; |
---|
371 | winrect.top = 80; |
---|
372 | winrect.right = winrect.left + window_width; |
---|
373 | winrect.bottom = winrect.top + window_height; |
---|
374 | DWORD style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; |
---|
375 | AdjustWindowRect (&winrect,style,1); |
---|
376 | char title[100]; |
---|
377 | sprintf (title,"Simulation test environment v%d.%02d", |
---|
378 | DS_VERSION >> 8,DS_VERSION & 0xff); |
---|
379 | main_window = CreateWindow ("SimAppClass",title,style, |
---|
380 | winrect.left,winrect.top,winrect.right-winrect.left,winrect.bottom-winrect.top, |
---|
381 | NULL,NULL,ghInstance,NULL); |
---|
382 | if (main_window==NULL) dsError ("could not create main window"); |
---|
383 | ShowWindow (main_window, gnCmdShow); |
---|
384 | |
---|
385 | HDC dc = GetDC (main_window); // get DC for this window |
---|
386 | if (dc==NULL) dsError ("could not get window DC"); |
---|
387 | |
---|
388 | // set pixel format for DC |
---|
389 | |
---|
390 | PIXELFORMATDESCRIPTOR pfd = { |
---|
391 | sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd |
---|
392 | 1, // version number |
---|
393 | PFD_DRAW_TO_WINDOW | // support window |
---|
394 | PFD_SUPPORT_OPENGL | // support OpenGL |
---|
395 | PFD_DOUBLEBUFFER, // double buffered |
---|
396 | PFD_TYPE_RGBA, // RGBA type |
---|
397 | 24, // 24-bit color depth |
---|
398 | 0, 0, 0, 0, 0, 0, // color bits ignored |
---|
399 | 0, // no alpha buffer |
---|
400 | 0, // shift bit ignored |
---|
401 | 0, // no accumulation buffer |
---|
402 | 0, 0, 0, 0, // accum bits ignored |
---|
403 | 32, // 32-bit z-buffer |
---|
404 | 0, // no stencil buffer |
---|
405 | 0, // no auxiliary buffer |
---|
406 | PFD_MAIN_PLANE, // main layer |
---|
407 | 0, // reserved |
---|
408 | 0, 0, 0 // layer masks ignored |
---|
409 | }; |
---|
410 | // get the best available match of pixel format for the device context |
---|
411 | int iPixelFormat = ChoosePixelFormat (dc,&pfd); |
---|
412 | if (iPixelFormat==0) |
---|
413 | dsError ("could not find a good OpenGL pixel format"); |
---|
414 | // set the pixel format of the device context |
---|
415 | if (SetPixelFormat (dc,iPixelFormat,&pfd)==FALSE) |
---|
416 | dsError ("could not set DC pixel format for OpenGL"); |
---|
417 | |
---|
418 | // ********** |
---|
419 | // start the rendering thread |
---|
420 | |
---|
421 | // set renderer globals |
---|
422 | renderer_dc = dc; |
---|
423 | renderer_width = window_width; |
---|
424 | renderer_height = window_height; |
---|
425 | renderer_fn = fn; |
---|
426 | |
---|
427 | DWORD threadId, thirdParam = 0; |
---|
428 | HANDLE hThread; |
---|
429 | |
---|
430 | hThread = CreateThread( |
---|
431 | NULL, // no security attributes |
---|
432 | 0, // use default stack size |
---|
433 | renderingThread, // thread function |
---|
434 | &thirdParam, // argument to thread function |
---|
435 | 0, // use default creation flags |
---|
436 | &threadId); // returns the thread identifier |
---|
437 | |
---|
438 | if (hThread==NULL) dsError ("Could not create rendering thread"); |
---|
439 | |
---|
440 | // ********** |
---|
441 | // start GUI message processing |
---|
442 | |
---|
443 | MSG msg; |
---|
444 | while (GetMessage (&msg,main_window,0,0)) { |
---|
445 | if (!TranslateAccelerator (main_window,accelerators,&msg)) { |
---|
446 | TranslateMessage (&msg); |
---|
447 | DispatchMessage (&msg); |
---|
448 | } |
---|
449 | } |
---|
450 | |
---|
451 | // terminate rendering thread |
---|
452 | renderer_run = 0; |
---|
453 | DWORD ret = WaitForSingleObject (hThread,2000); |
---|
454 | if (ret==WAIT_TIMEOUT) dsWarning ("Could not kill rendering thread (1)"); |
---|
455 | DWORD exitcode=0; |
---|
456 | if (!(GetExitCodeThread (hThread,&exitcode) && exitcode == 123)) |
---|
457 | dsWarning ("Could not kill rendering thread (2)"); |
---|
458 | CloseHandle (hThread); // dont need thread handle anymore |
---|
459 | |
---|
460 | // destroy window |
---|
461 | DestroyWindow (main_window); |
---|
462 | } |
---|
463 | |
---|
464 | |
---|
465 | extern "C" void dsStop() |
---|
466 | { |
---|
467 | // just calling PostQuitMessage() here wont work, as this function is |
---|
468 | // typically called from the rendering thread, not the GUI thread. |
---|
469 | // instead we must post the message to the GUI window explicitly. |
---|
470 | |
---|
471 | if (main_window) PostMessage (main_window,WM_QUIT,0,0); |
---|
472 | } |
---|
473 | |
---|
474 | |
---|
475 | extern "C" double dsElapsedTime() |
---|
476 | { |
---|
477 | static double prev=0.0; |
---|
478 | double curr = timeGetTime()/1000.0; |
---|
479 | if (!prev) |
---|
480 | prev=curr; |
---|
481 | double retval = curr-prev; |
---|
482 | prev=curr; |
---|
483 | if (retval>1.0) retval=1.0; |
---|
484 | if (retval<dEpsilon) retval=dEpsilon; |
---|
485 | return retval; |
---|
486 | } |
---|
487 | |
---|
488 | |
---|
489 | // JPerkins: if running as a DLL, grab my module handle at load time so |
---|
490 | // I can find the accelerators table later |
---|
491 | |
---|
492 | BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) |
---|
493 | { |
---|
494 | switch (fdwReason) |
---|
495 | { |
---|
496 | case DLL_PROCESS_ATTACH: |
---|
497 | ghInstance = hinstDLL; |
---|
498 | break; |
---|
499 | } |
---|
500 | return TRUE; |
---|
501 | } |
---|
502 | |
---|
503 | |
---|
504 | // JPerkins: the new build system can set the entry point of the tests to |
---|
505 | // main(); this code is no longer necessary |
---|
506 | /* |
---|
507 | |
---|
508 | //*************************************************************************** |
---|
509 | // windows entry point |
---|
510 | // |
---|
511 | // NOTE: WinMain is not guaranteed to be called with MinGW, because MinGW |
---|
512 | // always calls main if it is defined and most users of this library will |
---|
513 | // define their own main. So the startup functionality is kept in |
---|
514 | // zDriverStartup(), which is also called when dsSimulationLoop() is called. |
---|
515 | |
---|
516 | extern "C" int main (int argc, char **argv); |
---|
517 | |
---|
518 | |
---|
519 | int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, |
---|
520 | LPSTR lpCmdLine, int nCmdShow) |
---|
521 | { |
---|
522 | drawStuffStartup(); |
---|
523 | return main (0,0); // @@@ should really pass cmd line arguments |
---|
524 | } |
---|
525 | |
---|
526 | */ |
---|
527 | |
---|