]> icculus.org git repositories - taylor/freespace2.git/blob - src/osapi/os_win.cpp
Initial revision
[taylor/freespace2.git] / src / osapi / os_win.cpp
1 /*
2  * $Logfile: /Freespace2/code/OsApi/OsApi.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Low level Windows code
8  *
9  * $Log$
10  * Revision 1.1  2002/05/03 03:28:10  root
11  * Initial revision
12  * 
13  * 
14  * 7     6/30/99 5:53p Dave
15  * Put in new anti-camper code.
16  * 
17  * 6     6/03/99 6:37p Dave
18  * More TNT fun. Made perspective bitmaps more flexible.
19  * 
20  * 5     6/02/99 6:18p Dave
21  * Fixed TNT lockup problems! Wheeeee!
22  * 
23  * 4     12/18/98 1:13a Dave
24  * Rough 1024x768 support for Direct3D. Proper detection and usage through
25  * the launcher.
26  * 
27  * 3     10/09/98 2:57p Dave
28  * Starting splitting up OS stuff.
29  * 
30  * 2     10/08/98 2:38p Dave
31  * Cleanup up OsAPI code significantly. Removed old functions, centralized
32  * registry functions.
33  * 
34  * 118   7/10/98 5:04p Dave
35  * Fix connection speed bug on standalone server.
36  * 
37  * 117   5/24/98 2:28p Hoffoss
38  * Because we never really care about if the left or the right shift or
39  * alt key was used, but rather than either shift or alt was used, made
40  * both map to the left one.  Solves some problems, causes none.
41  * 
42  * 116   5/18/98 9:22p John
43  * Took out the annoying tab check.
44  * 
45  * 115   5/18/98 11:17a John
46  * Fixed some bugs with software window and output window.
47  * 
48  * 114   5/16/98 2:20p John
49  * Changed the os_suspend and resume to use a critical section to prevent
50  * threads from executing rather than just suspending the thread.  Had to
51  * make sure gr_close was called before os_close.
52  * 
53  * 113   5/15/98 4:49p John
54  * 
55  * 112   5/15/98 3:36p John
56  * Fixed bug with new graphics window code and standalone server.  Made
57  * hwndApp not be a global anymore.
58  * 
59  * 111   5/14/98 5:42p John
60  * Revamped the whole window position/mouse code for the graphics windows.
61  * 
62  * 110   5/04/98 11:08p Hoffoss
63  * Expanded on Force Feedback code, and moved it all into Joy_ff.cpp.
64  * Updated references everywhere to it.
65  *
66  * $NoKeywords: $
67  */
68
69 #include <windows.h>
70 #include <windowsx.h>
71 #include <commctrl.h>
72 #include <stdio.h>
73 #include <io.h>
74 #include <fcntl.h>
75 #include <winsock.h>
76 #include <stdarg.h>
77 #include <direct.h>
78
79 #include "pstypes.h"
80 #include "osapi.h"
81 #include "key.h"
82 #include "palman.h"
83 #include "mouse.h"
84 #include "outwnd.h"
85 #include "2d.h"
86 #include "cfile.h"
87 #include "sound.h"
88 #include "freespaceresource.h"
89 #include "managepilot.h"
90 #include "joy.h"
91 #include "joy_ff.h"
92 #include "gamesequence.h"
93 #include "freespace.h"
94 #include "osregistry.h"
95 #include "cmdline.h"
96
97 // ----------------------------------------------------------------------------------------------------
98 // OSAPI DEFINES/VARS
99 //
100
101 // os-wide globals
102 static HINSTANCE        hInstApp;
103 static HWND                     hwndApp = NULL;
104 static HANDLE           hThread=NULL;
105 static DWORD            ThreadID;
106 static int                      fAppActive = 0;
107 static int                      main_window_inited = 0;
108 static char                     szWinTitle[128];
109 static char                     szWinClass[128];
110 static int                      WinX, WinY, WinW, WinH;
111 static int                      Os_inited = 0;
112
113 static CRITICAL_SECTION Os_lock;
114
115 int Os_debugger_running = 0;
116
117 // ----------------------------------------------------------------------------------------------------
118 // OSAPI FORWARD DECLARATIONS
119 //
120
121 #ifdef THREADED
122         // thread handler for the main message thread
123         DWORD win32_process(DWORD lparam);
124 #else
125         DWORD win32_process1(DWORD lparam);
126         DWORD win32_process1(DWORD lparam);
127 #endif
128
129 // Fills in the Os_debugger_running with non-zero if debugger detected.
130 void os_check_debugger();
131
132 // called at shutdown. Makes sure all thread processing terminates.
133 void os_deinit();
134
135 // go through all windows and try and find the one that matches the search string
136 BOOL __stdcall os_enum_windows( HWND hwnd, char * search_string );
137
138 // message handler for the main thread
139 LRESULT CALLBACK win32_message_handler(HWND hwnd,UINT msg,WPARAM wParam, LPARAM lParam);
140
141 // create the main window
142 BOOL win32_create_window();
143
144
145 // ----------------------------------------------------------------------------------------------------
146 // OSAPI FUNCTIONS
147 //
148
149 // initialization/shutdown functions -----------------------------------------------
150
151 // If app_name is NULL or ommited, then TITLE is used
152 // for the app name, which is where registry keys are stored.
153 void os_init(char * wclass, char * title, char *app_name, char *version_string )
154 {
155         os_init_registry_stuff(Osreg_company_name, title, version_string);
156         
157         strcpy( szWinTitle, title );
158         strcpy( szWinClass, wclass );   
159
160         InitializeCriticalSection( &Os_lock );
161
162         #ifdef THREADED
163                 // Create an even to signal that the window is created, 
164                 // so that we don't return from this function until 
165                 // the window is all properly created.
166                 HANDLE Window_created = CreateEvent( NULL, FALSE, FALSE, NULL );
167                 hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)win32_process, Window_created, 0, &ThreadID );
168                 if ( WaitForSingleObject( Window_created, 5000 )==WAIT_TIMEOUT) {                       //INFINITE );
169                         mprintf(( "Wait timeout!\n" ));
170                 }
171                 CloseHandle(Window_created);
172                 Window_created = NULL;
173         #else
174                 win32_process1(0);
175         #endif // THREADED
176
177         // initialized
178         Os_inited = 1;
179
180         // check to see if we're running under msdev
181         os_check_debugger();
182
183         atexit(os_deinit);
184 }
185
186 // set the main window title
187 void os_set_title( char * title )
188 {
189         strcpy( szWinTitle, title );
190         SetWindowText( hwndApp, szWinTitle );
191 }
192
193 // call at program end
194 void os_cleanup()
195 {
196         // Tell the app to quit
197         PostMessage( hwndApp, WM_DESTROY, 0, 0 );
198         
199         #ifndef NDEBUG
200                 outwnd_close();
201         #endif
202 }
203
204
205 // window management -----------------------------------------------------------------
206
207 // Returns 1 if app is not the foreground app.
208 int os_foreground()
209 {
210         return fAppActive;
211 }
212
213 // Returns the handle to the main window
214 uint os_get_window()
215 {
216         return (uint)hwndApp;
217 }
218
219
220 // process management -----------------------------------------------------------------
221
222 // Sleeps for n milliseconds or until app becomes active.
223 void os_sleep(int ms)
224 {
225         Sleep(ms);
226 }
227
228 // Used to stop message processing
229 void os_suspend()
230 {
231         ENTER_CRITICAL_SECTION(&Os_lock);       
232 }
233
234 // resume message processing
235 void os_resume()
236 {
237         LEAVE_CRITICAL_SECTION(&Os_lock);       
238 }
239
240
241 // ----------------------------------------------------------------------------------------------------
242 // OSAPI FORWARD DECLARATIONS
243 //
244
245 #pragma warning(disable:4702)
246 #ifdef THREADED
247
248 // thread handler for the main message thread
249 DWORD win32_process(DWORD lparam)
250 {
251         MSG msg;
252         HANDLE Window_created = (HANDLE)lparam;
253
254         if ( !win32_create_window() )
255                 return 0;
256
257         // Let the app continue once the window is created
258         SetEvent(Window_created);
259
260         while (1)       {       
261                 if (WaitMessage() == TRUE)      {
262                         EnterCriticalSection(&Os_lock);                 
263                         while(PeekMessage(&msg,0,0,0,PM_REMOVE))        {
264                                 if ( msg.message == WM_DESTROY )        {
265                                         LeaveCriticalSection(&Os_lock);
266
267                                         // cleanup and exit this thread!!
268                                         DeleteCriticalSection(&Os_lock);
269                                         return 0;
270                                 }
271                                 TranslateMessage(&msg);
272                                 DispatchMessage(&msg);
273                         }
274                         LeaveCriticalSection(&Os_lock);
275                 } 
276         }
277         return 0;
278 }
279
280 #else
281 DWORD win32_process1(DWORD lparam)
282 {
283         if ( !win32_create_window() )
284                 return 0;
285
286         return 0;
287 }
288
289
290 DWORD win32_process2(DWORD lparam)
291 {
292         MSG msg;
293
294         while(PeekMessage(&msg,0,0,0,PM_REMOVE))        {
295                 TranslateMessage(&msg);
296                 DispatchMessage(&msg);
297         }
298
299         return 0;
300 }
301 #endif // THREADED
302 #pragma warning(default:4702)
303
304 // Fills in the Os_debugger_running with non-zero if debugger detected.
305 void os_check_debugger()
306 {
307         HMODULE hMod;
308         char search_string[256];
309         char myname[128];
310         int namelen;
311         char * p;
312
313         Os_debugger_running = 0;                // Assume its not
314
315         // Find my EXE file name
316         hMod = GetModuleHandle(NULL);
317         if ( !hMod ) return;
318         namelen = GetModuleFileName( hMod, myname, 127 );       
319         if ( namelen < 1 ) return;
320         
321         // Strip off the .EXE
322         p = strstr( myname, ".exe" );
323         if (!p) return;
324         *p = '\0';
325
326         // Move p to point to first letter of EXE filename
327         while( (*p!='\\') && (*p!='/') && (*p!=':') )
328                 p--;
329         p++;    
330         if ( strlen(p) < 1 ) return;
331
332         // Build what the debugger's window title would be if the debugger is running...
333         sprintf( search_string, "[run] - %s -", p );
334
335         // ... and then search for it.
336         EnumWindows( (int (__stdcall *)(struct HWND__ *,long))os_enum_windows, (long)&search_string );
337 }
338
339 // called at shutdown. Makes sure all thread processing terminates.
340 void os_deinit()
341 {
342         if (hThread)    {
343                 CloseHandle(hThread);
344                 hThread = NULL;
345         }
346 }
347
348 // go through all windows and try and find the one that matches the search string
349 BOOL __stdcall os_enum_windows( HWND hwnd, char * search_string )
350 {
351         char tmp[128];
352         int len;
353
354         len = GetWindowText( hwnd, tmp, 127 );
355          
356         if ( len )      {
357                 if ( strstr( tmp, search_string ))      {
358                         Os_debugger_running = 1;                // found the search string!
359                         return FALSE;   // stop enumerating windows
360                 }
361         }
362
363         return TRUE;    // continue enumeration
364 }
365
366
367 int Got_message = 0;
368 // message handler for the main thread
369 LRESULT CALLBACK win32_message_handler(HWND hwnd,UINT msg,WPARAM wParam, LPARAM lParam)
370 {
371         // Got_message++;
372
373         switch(msg)     {
374
375         case WM_QUERYNEWPALETTE:
376                 // mprintf(( "WM: QueryNewPalette\n" ));
377                 return TRUE;    // Say that I've realized my own palette
378                 break;
379         case WM_PALETTECHANGED:
380                 // mprintf(( "WM: PaletteChanged\n" ));
381                 break;
382         case WM_PALETTEISCHANGING:
383                 // mprintf(( "WM: PaletteIsChanging\n" ));
384                 break;
385
386         case WM_DISPLAYCHANGE:
387                 // mprintf(( "WM: DisplayChange\n" ));
388                 break;
389
390         case WM_LBUTTONDOWN:
391                 mouse_mark_button( MOUSE_LEFT_BUTTON, 1 );
392                 break;
393
394         case WM_LBUTTONUP:
395                 mouse_mark_button( MOUSE_LEFT_BUTTON, 0 );
396                 break;
397
398         case WM_RBUTTONDOWN:
399                 mouse_mark_button( MOUSE_RIGHT_BUTTON, 1 );
400                 break;
401
402         case WM_RBUTTONUP:
403                 mouse_mark_button( MOUSE_RIGHT_BUTTON, 0 );
404                 break;
405
406         case WM_MBUTTONDOWN:
407                 mouse_mark_button( MOUSE_MIDDLE_BUTTON, 1 );
408                 break;
409
410         case WM_MBUTTONUP:
411                 mouse_mark_button( MOUSE_MIDDLE_BUTTON, 0 );
412                 break;
413
414         case WM_TIMER:
415                 break;
416
417         case WM_SYSKEYDOWN:
418         case WM_KEYDOWN:        {               
419                         int nVirtKey;
420                         uint lKeyData;
421
422                         int latency;
423                         latency = timeGetTime() - GetMessageTime();
424                         if ( latency < 0 )
425                                 latency=0;
426
427                         nVirtKey = (int)wParam;    // virtual-key code 
428                         lKeyData = (lParam>>16) & 255;          // key data 
429                         if ( (lParam>>16) & 256 ) lKeyData += 0x80;
430
431                         // Fix up print screen, whose OEM code is wrong under 95.
432                         if ( nVirtKey == VK_SNAPSHOT )  {
433                                 lKeyData = KEY_PRINT_SCRN;      
434                         }
435
436                         if (lKeyData == KEY_RSHIFT)  // either shift is just a shift to us..
437                                 lKeyData = KEY_LSHIFT;
438
439                         if (lKeyData == KEY_RALT)  // Same with alt keys..
440                                 lKeyData = KEY_LALT;
441
442 //                      mprintf(( "Key down = 0x%x|%x\n", lKeyData, nVirtKey ));
443                         key_mark( lKeyData, 1, latency );
444 //                      mprintf(( "Key down = 0x%x\n", lKeyData ));
445                         //Warning( LOCATION, "Key = 0x%x", lKeyData );                  
446                 }
447                 break;
448
449         case WM_SYSKEYUP:
450         case WM_KEYUP:   {              
451                         int nVirtKey;
452                         uint lKeyData;
453
454                         int latency;
455                         latency = timeGetTime() - GetMessageTime();
456                         if ( latency < 0 )
457                                 latency=0;
458
459                         nVirtKey = (int) wParam;    // virtual-key code 
460                         lKeyData = (lParam>>16) & 255;          // key data 
461                         if ( (lParam>>16) & 256 ) lKeyData += 0x80;
462
463                         // Fix up print screen, whose OEM code is wrong under 95.
464                         if ( nVirtKey == VK_SNAPSHOT )  {
465                                 lKeyData = KEY_PRINT_SCRN;      
466                         }
467
468                         if (lKeyData == KEY_RSHIFT)  // either shift is just a shift to us..
469                                 lKeyData = KEY_LSHIFT;
470
471                         if (lKeyData == KEY_RALT)  // Same with alt keys..
472                                 lKeyData = KEY_LALT;
473
474 //                      mprintf(( "Key up = 0x%x|%x\n", lKeyData, nVirtKey ));
475                         if ( lKeyData == 0xB7 ) {
476                                 // Hack for PrintScreen which only sends one up message!
477                                 key_mark( lKeyData, 1, latency );               
478                                 key_mark( lKeyData, 0, latency );
479
480                         } else {
481                                 key_mark( lKeyData, 0, latency );
482                         }                       
483                 }               
484                 break;
485
486         case WM_KILLFOCUS:
487                 key_lost_focus();
488
489                 gr_activate(0);
490                 break;
491
492         case WM_SETFOCUS:
493                 key_got_focus();
494
495                 gr_activate(1);
496                 break;
497
498         case WM_ACTIVATE:                       //APP:  //
499         case WM_ACTIVATEAPP:
500                 {
501                         BOOL OldfAppActive = fAppActive;
502                         
503                         // The application z-ordering has changed. If we are now the
504                         // foreground application wParm will be TRUE.
505                         if ( msg == WM_ACTIVATE )       {
506                                 if ( (LOWORD(wParam) == WA_ACTIVE) || (LOWORD(wParam)==WA_CLICKACTIVE)) {
507                                         fAppActive = TRUE;      //(BOOL)wParam;
508                                 } else {
509                                         fAppActive = FALSE;     //(BOOL)wParam;
510                                 }
511
512                         } else {
513                                 fAppActive = (BOOL)wParam;
514                         }
515
516                         //mprintf(( "Activate: %d\n", fAppActive ));
517
518                         if ( OldfAppActive != fAppActive )      {
519
520                                 if ( fAppActive )       {
521                                         // maximize it
522                                         //      mprintf(( "Priority: HIGH\n" ));
523                                         joy_reacquire_ff();
524
525 #ifdef THREADED
526                                         SetThreadPriority( hThread, THREAD_PRIORITY_HIGHEST );
527 #endif
528
529                                         gr_activate(fAppActive);
530                                         //SetThreadPriority( hThread, THREAD_PRIORITY_TIME_CRITICAL );
531                                 } else {
532                                         //mprintf(( "Priority: LOW\n" ));
533                                         joy_unacquire_ff();
534                                         if (Mouse_hidden)       {
535                                                 Mouse_hidden=0;
536                                                 //ShowCursor(1);
537                                                 //mprintf(( "Mouse:Alive\n" ));         
538                                         }
539                                         // minimize it
540                                         // key_outkey( KEY_PAUSE );
541
542 #ifdef THREADED
543                                         SetThreadPriority( hThread, THREAD_PRIORITY_NORMAL );
544 #endif
545                                         gr_activate(fAppActive);
546                                 }
547                         }
548                 }
549                 break;
550
551         case WM_DESTROY:
552                 // mprintf(( "WM_DESTROY called\n" ));
553                 PostQuitMessage(0);
554                 break;
555
556         case WM_CLOSE:
557                 gameseq_post_event(GS_EVENT_QUIT_GAME);
558                 break;
559
560         case WM_SYSCOMMAND:
561                 // mprintf(( "Sys command called '%x'\n", wParam ));
562                  if ( wParam != SC_SCREENSAVE ){
563                          return DefWindowProc(hwnd, msg, wParam, lParam);
564                  }
565                  break;
566
567 /*
568         case MM_WIM_DATA:
569                 rtvoice_stream_data((uint)hwnd, (uint)wParam, (uint)lParam);
570                 break;
571 */
572
573         default:
574                 return DefWindowProc(hwnd, msg, wParam, lParam);
575                 break;
576         }
577
578         return 0;
579 }
580
581 // create the main window
582 BOOL win32_create_window()
583 {
584         int windowed = 0;
585         int hires = 0;
586         char *ptr = os_config_read_string(NULL, NOX("Videocard"), NULL);        
587         if(ptr && strstr(ptr, NOX("Direct 3D -")) && Cmdline_window){
588                 windowed = 1;
589         }
590         if(ptr && strstr(ptr, NOX("1024")) && Cmdline_window){
591                 hires = 1;
592         }
593
594         WNDCLASSEX wclass;
595         HINSTANCE hInst = GetModuleHandle(NULL);
596
597         wclass.hInstance                = hInst;
598         wclass.lpszClassName = szWinClass;
599         wclass.lpfnWndProc      = (WNDPROC)win32_message_handler;               
600         if(windowed){
601                 wclass.style                    = CS_VREDRAW | CS_HREDRAW | CS_OWNDC;
602         } else {
603                 wclass.style                    = CS_BYTEALIGNCLIENT|CS_VREDRAW | CS_HREDRAW;   // | CS_OWNDC;  //CS_DBLCLKS | CS_PARENTDC| CS_VREDRAW | CS_HREDRAW |;  
604         }               
605         wclass.cbSize                   = sizeof(WNDCLASSEX);
606         wclass.hIcon                    = LoadIcon(hInst, MAKEINTRESOURCE(IDI_APP_ICON) );
607         wclass.hIconSm                  = NULL; //LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON1) );
608         wclass.hCursor                  = LoadCursor(NULL, IDC_ARROW);
609         wclass.lpszMenuName     = NULL; //"FreeSpaceMenu";
610         wclass.cbClsExtra               = 0;
611         wclass.cbWndExtra               = 0;
612         wclass.hbrBackground = (HBRUSH)NULL;
613
614         if (!RegisterClassEx(&wclass)) return FALSE;
615         
616         int style;
617         if(windowed){
618                 style = WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
619         } else {
620                 style = WS_POPUP;
621         }       
622
623         // make sure we adjust for the actual window border     
624         int x_add = 0;
625         int y_add = 0;
626         x_add += GetSystemMetrics(SM_CXFIXEDFRAME) * 2;
627         y_add += GetSystemMetrics(SM_CYFIXEDFRAME) + GetSystemMetrics(SM_CYCAPTION);
628
629         // Make a 32x32 window.  It never should get shown, because the graphics code
630         // will then size it.
631         if(windowed){
632                 hwndApp = CreateWindow( szWinClass, szWinTitle,
633                                                                         style,   
634                                                                         CW_USEDEFAULT,
635                                                                         CW_USEDEFAULT,
636                                                                         hires ? 1024 : 640 + x_add,
637                                                                         hires ? 768 : 480 + y_add,
638                                                                         NULL, (HMENU)NULL, hInst, (LPSTR)NULL); 
639         } else {
640                 // Make a 32x32 window.  It never should get shown, because the graphics code
641                 // will then size it.
642                 hwndApp = CreateWindow( szWinClass, szWinTitle,
643                                                                         style,   
644                                                                         (GetSystemMetrics( SM_CXSCREEN )-32 )/2,        //x
645                                                                         (GetSystemMetrics( SM_CYSCREEN )-32 )/2,        //y
646                                                                         32,32,                                                                  // wh
647                                                                         NULL, (HMENU)NULL, hInst, (LPSTR)NULL); 
648         }
649
650         // Hack!! Turn off Window's cursor.
651         ShowCursor(0);
652         ClipCursor(NULL);
653
654         main_window_inited = 1;
655         #ifndef NDEBUG
656                 outwnd_init(1);
657         #endif  
658
659         if(windowed){
660                 ShowWindow( hwndApp, SW_SHOWNORMAL );
661                 UpdateWindow( hwndApp );
662         }
663
664         return TRUE;
665 }
666
667 void os_poll()
668 {
669 #ifndef THREADED
670         win32_process2(0);
671 #else
672         MSG msg;
673         EnterCriticalSection(&Os_lock);
674         while(PeekMessage(&msg,0,0,0,PM_NOREMOVE))      {               
675                 if ( msg.message == WM_DESTROY )        {
676                         break;
677                 }
678                 if (PeekMessage(&msg,0,0,0,PM_REMOVE))  {
679                         TranslateMessage(&msg);
680                         DispatchMessage(&msg);
681                 }               
682                 Got_message++;
683         }
684         LeaveCriticalSection(&Os_lock);
685 #endif
686 }
687
688 void debug_int3()
689 {
690         gr_activate(0);
691         _asm { int 3 };
692         gr_activate(1);
693 }