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