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