]> icculus.org git repositories - divverent/darkplaces.git/blob - vid_wgl.c
expanded stats reporting (when in developer mode) of how many entities were processed...
[divverent/darkplaces.git] / vid_wgl.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // gl_vidnt.c -- NT GL vid component
21
22 #include "quakedef.h"
23 #include "winquake.h"
24 #include "resource.h"
25 #include <commctrl.h>
26
27 int cl_available = true;
28
29 int (WINAPI *qwglChoosePixelFormat)(HDC, CONST PIXELFORMATDESCRIPTOR *);
30 int (WINAPI *qwglDescribePixelFormat)(HDC, int, UINT, LPPIXELFORMATDESCRIPTOR);
31 //int (WINAPI *qwglGetPixelFormat)(HDC);
32 BOOL (WINAPI *qwglSetPixelFormat)(HDC, int, CONST PIXELFORMATDESCRIPTOR *);
33 BOOL (WINAPI *qwglSwapBuffers)(HDC);
34 HGLRC (WINAPI *qwglCreateContext)(HDC);
35 BOOL (WINAPI *qwglDeleteContext)(HGLRC);
36 HGLRC (WINAPI *qwglGetCurrentContext)(VOID);
37 HDC (WINAPI *qwglGetCurrentDC)(VOID);
38 PROC (WINAPI *qwglGetProcAddress)(LPCSTR);
39 BOOL (WINAPI *qwglMakeCurrent)(HDC, HGLRC);
40 BOOL (WINAPI *qwglSwapIntervalEXT)(int interval);
41 const char *(WINAPI *qwglGetExtensionsStringARB)(HDC hdc);
42
43 static dllfunction_t wglfuncs[] =
44 {
45         {"wglChoosePixelFormat", (void **) &qwglChoosePixelFormat},
46         {"wglDescribePixelFormat", (void **) &qwglDescribePixelFormat},
47 //      {"wglGetPixelFormat", (void **) &qwglGetPixelFormat},
48         {"wglSetPixelFormat", (void **) &qwglSetPixelFormat},
49         {"wglSwapBuffers", (void **) &qwglSwapBuffers},
50         {"wglCreateContext", (void **) &qwglCreateContext},
51         {"wglDeleteContext", (void **) &qwglDeleteContext},
52         {"wglGetProcAddress", (void **) &qwglGetProcAddress},
53         {"wglMakeCurrent", (void **) &qwglMakeCurrent},
54         {"wglGetCurrentContext", (void **) &qwglGetCurrentContext},
55         {"wglGetCurrentDC", (void **) &qwglGetCurrentDC},
56         {NULL, NULL}
57 };
58
59 static dllfunction_t wglswapintervalfuncs[] =
60 {
61         {"wglSwapIntervalEXT", (void **) &qwglSwapIntervalEXT},
62         {NULL, NULL}
63 };
64
65 qboolean scr_skipupdate;
66
67 static DEVMODE gdevmode;
68 static qboolean vid_initialized = false;
69 static qboolean vid_wassuspended = false;
70 static int vid_usingmouse;
71 extern qboolean mouseactive;  // from in_win.c
72 static HICON hIcon;
73
74 HWND mainwindow;
75
76 //HWND WINAPI InitializeWindow (HINSTANCE hInstance, int nCmdShow);
77
78 static int vid_isfullscreen;
79
80 //void VID_MenuDraw (void);
81 //void VID_MenuKey (int key);
82
83 //LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
84 //void AppActivate(BOOL fActive, BOOL minimize);
85 //void ClearAllStates (void);
86 //void VID_UpdateWindowStatus (void);
87
88 //====================================
89
90 int window_x, window_y, window_width, window_height;
91 int window_center_x, window_center_y;
92
93 void IN_ShowMouse (void);
94 void IN_DeactivateMouse (void);
95 void IN_HideMouse (void);
96 void IN_ActivateMouse (void);
97 void IN_MouseEvent (int mstate);
98 void IN_UpdateClipCursor (void);
99
100 qboolean mouseinitialized;
101 static qboolean dinput;
102
103 // input code
104
105 #include <dinput.h>
106
107 #define DINPUT_BUFFERSIZE           16
108 #define iDirectInputCreate(a,b,c,d)     pDirectInputCreate(a,b,c,d)
109
110 HRESULT (WINAPI *pDirectInputCreate)(HINSTANCE hinst, DWORD dwVersion,
111         LPDIRECTINPUT * lplpDirectInput, LPUNKNOWN punkOuter);
112
113 // mouse variables
114 int                     mouse_buttons;
115 int                     mouse_oldbuttonstate;
116 POINT           current_pos;
117 int                     mouse_x, mouse_y, old_mouse_x, old_mouse_y, mx_accum, my_accum;
118
119 static qboolean restore_spi;
120 static int              originalmouseparms[3], newmouseparms[3] = {0, 0, 1};
121
122 unsigned int uiWheelMessage;
123 qboolean        mouseactive;
124 //qboolean              mouseinitialized;
125 static qboolean mouseparmsvalid, mouseactivatetoggle;
126 static qboolean mouseshowtoggle = 1;
127 static qboolean dinput_acquired;
128
129 static unsigned int             mstate_di;
130
131 // joystick defines and variables
132 // where should defines be moved?
133 #define JOY_ABSOLUTE_AXIS       0x00000000              // control like a joystick
134 #define JOY_RELATIVE_AXIS       0x00000010              // control like a mouse, spinner, trackball
135 #define JOY_MAX_AXES            6                               // X, Y, Z, R, U, V
136 #define JOY_AXIS_X                      0
137 #define JOY_AXIS_Y                      1
138 #define JOY_AXIS_Z                      2
139 #define JOY_AXIS_R                      3
140 #define JOY_AXIS_U                      4
141 #define JOY_AXIS_V                      5
142
143 enum _ControlList
144 {
145         AxisNada = 0, AxisForward, AxisLook, AxisSide, AxisTurn
146 };
147
148 DWORD   dwAxisFlags[JOY_MAX_AXES] =
149 {
150         JOY_RETURNX, JOY_RETURNY, JOY_RETURNZ, JOY_RETURNR, JOY_RETURNU, JOY_RETURNV
151 };
152
153 DWORD   dwAxisMap[JOY_MAX_AXES];
154 DWORD   dwControlMap[JOY_MAX_AXES];
155 PDWORD  pdwRawValue[JOY_MAX_AXES];
156
157 // none of these cvars are saved over a session
158 // this means that advanced controller configuration needs to be executed
159 // each time.  this avoids any problems with getting back to a default usage
160 // or when changing from one controller to another.  this way at least something
161 // works.
162 cvar_t  in_joystick = {CVAR_SAVE, "joystick","0"};
163 cvar_t  joy_name = {0, "joyname", "joystick"};
164 cvar_t  joy_advanced = {0, "joyadvanced", "0"};
165 cvar_t  joy_advaxisx = {0, "joyadvaxisx", "0"};
166 cvar_t  joy_advaxisy = {0, "joyadvaxisy", "0"};
167 cvar_t  joy_advaxisz = {0, "joyadvaxisz", "0"};
168 cvar_t  joy_advaxisr = {0, "joyadvaxisr", "0"};
169 cvar_t  joy_advaxisu = {0, "joyadvaxisu", "0"};
170 cvar_t  joy_advaxisv = {0, "joyadvaxisv", "0"};
171 cvar_t  joy_forwardthreshold = {0, "joyforwardthreshold", "0.15"};
172 cvar_t  joy_sidethreshold = {0, "joysidethreshold", "0.15"};
173 cvar_t  joy_pitchthreshold = {0, "joypitchthreshold", "0.15"};
174 cvar_t  joy_yawthreshold = {0, "joyyawthreshold", "0.15"};
175 cvar_t  joy_forwardsensitivity = {0, "joyforwardsensitivity", "-1.0"};
176 cvar_t  joy_sidesensitivity = {0, "joysidesensitivity", "-1.0"};
177 cvar_t  joy_pitchsensitivity = {0, "joypitchsensitivity", "1.0"};
178 cvar_t  joy_yawsensitivity = {0, "joyyawsensitivity", "-1.0"};
179 cvar_t  joy_wwhack1 = {0, "joywwhack1", "0.0"};
180 cvar_t  joy_wwhack2 = {0, "joywwhack2", "0.0"};
181
182 qboolean        joy_avail, joy_advancedinit, joy_haspov;
183 DWORD           joy_oldbuttonstate, joy_oldpovstate;
184
185 int                     joy_id;
186 DWORD           joy_flags;
187 DWORD           joy_numbuttons;
188
189 static LPDIRECTINPUT            g_pdi;
190 static LPDIRECTINPUTDEVICE      g_pMouse;
191
192 static JOYINFOEX        ji;
193
194 static HINSTANCE hInstDI;
195
196 //static qboolean       dinput;
197
198 typedef struct MYDATA {
199         LONG  lX;                   // X axis goes here
200         LONG  lY;                   // Y axis goes here
201         LONG  lZ;                   // Z axis goes here
202         BYTE  bButtonA;             // One button goes here
203         BYTE  bButtonB;             // Another button goes here
204         BYTE  bButtonC;             // Another button goes here
205         BYTE  bButtonD;             // Another button goes here
206 } MYDATA;
207
208 static DIOBJECTDATAFORMAT rgodf[] = {
209   { &GUID_XAxis,    FIELD_OFFSET(MYDATA, lX),       DIDFT_AXIS | DIDFT_ANYINSTANCE,   0,},
210   { &GUID_YAxis,    FIELD_OFFSET(MYDATA, lY),       DIDFT_AXIS | DIDFT_ANYINSTANCE,   0,},
211   { &GUID_ZAxis,    FIELD_OFFSET(MYDATA, lZ),       0x80000000 | DIDFT_AXIS | DIDFT_ANYINSTANCE,   0,},
212   { 0,              FIELD_OFFSET(MYDATA, bButtonA), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
213   { 0,              FIELD_OFFSET(MYDATA, bButtonB), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
214   { 0,              FIELD_OFFSET(MYDATA, bButtonC), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
215   { 0,              FIELD_OFFSET(MYDATA, bButtonD), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
216 };
217
218 #define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))
219
220 static DIDATAFORMAT     df = {
221         sizeof(DIDATAFORMAT),       // this structure
222         sizeof(DIOBJECTDATAFORMAT), // size of object data format
223         DIDF_RELAXIS,               // absolute axis coordinates
224         sizeof(MYDATA),             // device data size
225         NUM_OBJECTS,                // number of objects
226         rgodf,                      // and here they are
227 };
228
229 // forward-referenced functions
230 void IN_StartupJoystick (void);
231 void Joy_AdvancedUpdate_f (void);
232 void IN_JoyMove (usercmd_t *cmd);
233 void IN_StartupMouse (void);
234
235 /*
236 ================
237 VID_UpdateWindowStatus
238 ================
239 */
240 void VID_UpdateWindowStatus (void)
241 {
242         window_center_x = window_x + window_width / 2;
243         window_center_y = window_y + window_height / 2;
244
245         if (mouseinitialized && mouseactive && !dinput)
246         {
247                 RECT window_rect;
248                 window_rect.left = window_x;
249                 window_rect.top = window_y;
250                 window_rect.right = window_x + window_width;
251                 window_rect.bottom = window_y + window_height;
252                 ClipCursor (&window_rect);
253         }
254 }
255
256
257 //====================================
258
259 /*
260 =================
261 VID_GetWindowSize
262 =================
263 */
264 void VID_GetWindowSize (int *x, int *y, int *width, int *height)
265 {
266         *x = 0;
267         *y = 0;
268         *width = window_width;
269         *height = window_height;
270 }
271
272
273 void VID_Finish (void)
274 {
275         HDC hdc;
276         int vid_usemouse;
277         if (r_render.integer && !scr_skipupdate)
278         {
279                 qglFinish();
280                 hdc = GetDC(mainwindow);
281                 SwapBuffers(hdc);
282                 ReleaseDC(mainwindow, hdc);
283         }
284
285 // handle the mouse state when windowed if that's changed
286         vid_usemouse = false;
287         if (vid_mouse.integer && !key_consoleactive)
288                 vid_usemouse = true;
289         if (vid_isfullscreen)
290                 vid_usemouse = true;
291         if (!vid_activewindow)
292                 vid_usemouse = false;
293         if (vid_usemouse)
294         {
295                 if (!vid_usingmouse)
296                 {
297                         vid_usingmouse = true;
298                         IN_ActivateMouse ();
299                         IN_HideMouse();
300                 }
301         }
302         else
303         {
304                 if (vid_usingmouse)
305                 {
306                         vid_usingmouse = false;
307                         IN_DeactivateMouse ();
308                         IN_ShowMouse();
309                 }
310         }
311 }
312
313 //==========================================================================
314
315
316
317
318 qbyte scantokey[128] =
319 {
320 //  0           1       2    3     4     5       6       7      8         9      A          B           C       D           E           F
321         0          ,27    ,'1'  ,'2'  ,'3'  ,'4'    ,'5'    ,'6'   ,'7'      ,'8'   ,'9'       ,'0'        ,'-'   ,'='         ,K_BACKSPACE,9    ,//0
322         'q'        ,'w'   ,'e'  ,'r'  ,'t'  ,'y'    ,'u'    ,'i'   ,'o'      ,'p'   ,'['       ,']'        ,13    ,K_CTRL      ,'a'        ,'s'  ,//1
323         'd'        ,'f'   ,'g'  ,'h'  ,'j'  ,'k'    ,'l'    ,';'   ,'\''     ,'`'   ,K_SHIFT   ,'\\'       ,'z'   ,'x'         ,'c'        ,'v'  ,//2
324         'b'        ,'n'   ,'m'  ,','  ,'.'  ,'/'    ,K_SHIFT,'*'   ,K_ALT    ,' '   ,0         ,K_F1       ,K_F2  ,K_F3        ,K_F4       ,K_F5 ,//3
325         K_F6       ,K_F7  ,K_F8 ,K_F9 ,K_F10,K_PAUSE,0      ,K_HOME,K_UPARROW,K_PGUP,K_KP_MINUS,K_LEFTARROW,K_KP_5,K_RIGHTARROW,K_KP_PLUS  ,K_END,//4
326         K_DOWNARROW,K_PGDN,K_INS,K_DEL,0    ,0      ,0      ,K_F11 ,K_F12    ,0     ,0         ,0          ,0     ,0           ,0          ,0    ,//5
327         0          ,0     ,0    ,0    ,0    ,0      ,0      ,0     ,0        ,0     ,0         ,0          ,0     ,0           ,0          ,0    ,//6
328         0          ,0     ,0    ,0    ,0    ,0      ,0      ,0     ,0        ,0     ,0         ,0          ,0     ,0           ,0          ,0     //7
329 };
330
331
332 /*
333 =======
334 MapKey
335
336 Map from windows to quake keynums
337 =======
338 */
339 int MapKey (int key, int virtualkey)
340 {
341         int result;
342         int modified = (key >> 16) & 255;
343         qboolean is_extended = false;
344
345         if (modified < 128 && scantokey[modified])
346                 result = scantokey[modified];
347         else
348         {
349                 result = 0;
350                 Con_DPrintf("key 0x%02x (0x%8x, 0x%8x) has no translation\n", modified, key, virtualkey);
351         }
352
353         if (key & (1 << 24))
354                 is_extended = true;
355
356         if ( !is_extended )
357         {
358                 switch ( result )
359                 {
360                 case K_HOME:
361                         return K_KP_HOME;
362                 case K_UPARROW:
363                         return K_KP_UPARROW;
364                 case K_PGUP:
365                         return K_KP_PGUP;
366                 case K_LEFTARROW:
367                         return K_KP_LEFTARROW;
368                 case K_RIGHTARROW:
369                         return K_KP_RIGHTARROW;
370                 case K_END:
371                         return K_KP_END;
372                 case K_DOWNARROW:
373                         return K_KP_DOWNARROW;
374                 case K_PGDN:
375                         return K_KP_PGDN;
376                 case K_INS:
377                         return K_KP_INS;
378                 case K_DEL:
379                         return K_KP_DEL;
380                 default:
381                         return result;
382                 }
383         }
384         else
385         {
386                 switch ( result )
387                 {
388                 case 0x0D:
389                         return K_KP_ENTER;
390                 case 0x2F:
391                         return K_KP_SLASH;
392                 case 0xAF:
393                         return K_KP_PLUS;
394                 }
395                 return result;
396         }
397 }
398
399 /*
400 ===================================================================
401
402 MAIN WINDOW
403
404 ===================================================================
405 */
406
407 /*
408 ================
409 ClearAllStates
410 ================
411 */
412 void ClearAllStates (void)
413 {
414         int             i;
415
416 // send an up event for each key, to make sure the server clears them all
417         for (i=0 ; i<256 ; i++)
418         {
419                 Key_Event (i, false);
420         }
421
422         Key_ClearStates ();
423         IN_ClearStates ();
424 }
425
426 extern qboolean host_loopactive;
427
428 void AppActivate(BOOL fActive, BOOL minimize)
429 /****************************************************************************
430 *
431 * Function:     AppActivate
432 * Parameters:   fActive - True if app is activating
433 *
434 * Description:  If the application is activating, then swap the system
435 *               into SYSPAL_NOSTATIC mode so that our palettes will display
436 *               correctly.
437 *
438 ****************************************************************************/
439 {
440         static BOOL     sound_active;
441
442         vid_activewindow = fActive;
443         vid_hidden = minimize;
444
445 // enable/disable sound on focus gain/loss
446         if (!vid_activewindow && sound_active)
447         {
448                 S_BlockSound ();
449                 sound_active = false;
450         }
451         else if (vid_activewindow && !sound_active)
452         {
453                 S_UnblockSound ();
454                 sound_active = true;
455         }
456
457         if (fActive)
458         {
459                 vid_allowhwgamma = true;
460                 if (vid_isfullscreen)
461                 {
462                         if (vid_wassuspended)
463                         {
464                                 vid_wassuspended = false;
465                                 ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN);
466                                 ShowWindow(mainwindow, SW_SHOWNORMAL);
467                         }
468
469                         // LordHavoc: from dabb, fix for alt-tab bug in NVidia drivers
470                         MoveWindow(mainwindow,0,0,gdevmode.dmPelsWidth,gdevmode.dmPelsHeight,false);
471                 }
472         }
473
474         if (!fActive)
475         {
476                 vid_allowhwgamma = false;
477                 vid_usingmouse = false;
478                 IN_DeactivateMouse ();
479                 IN_ShowMouse ();
480                 if (vid_isfullscreen)
481                 {
482                         ChangeDisplaySettings (NULL, 0);
483                         vid_wassuspended = true;
484                 }
485                 VID_RestoreSystemGamma();
486         }
487 }
488
489 LONG CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
490
491 /* main window procedure */
492 LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM  wParam, LPARAM lParam)
493 {
494         LONG    lRet = 1;
495         int             fActive, fMinimized, temp;
496         extern unsigned int uiWheelMessage;
497
498         if ( uMsg == uiWheelMessage )
499                 uMsg = WM_MOUSEWHEEL;
500
501         switch (uMsg)
502         {
503                 case WM_KILLFOCUS:
504                         if (vid_isfullscreen)
505                                 ShowWindow(mainwindow, SW_SHOWMINNOACTIVE);
506                         break;
507
508                 case WM_CREATE:
509                         break;
510
511                 case WM_MOVE:
512                         window_x = (int) LOWORD(lParam);
513                         window_y = (int) HIWORD(lParam);
514                         VID_UpdateWindowStatus ();
515                         break;
516
517                 case WM_KEYDOWN:
518                 case WM_SYSKEYDOWN:
519                         Key_Event (MapKey(lParam, wParam), true);
520                         break;
521
522                 case WM_KEYUP:
523                 case WM_SYSKEYUP:
524                         Key_Event (MapKey(lParam, wParam), false);
525                         break;
526
527                 case WM_SYSCHAR:
528                 // keep Alt-Space from happening
529                         break;
530
531         // this is complicated because Win32 seems to pack multiple mouse events into
532         // one update sometimes, so we always check all states and look for events
533                 case WM_LBUTTONDOWN:
534                 case WM_LBUTTONUP:
535                 case WM_RBUTTONDOWN:
536                 case WM_RBUTTONUP:
537                 case WM_MBUTTONDOWN:
538                 case WM_MBUTTONUP:
539                 case WM_MOUSEMOVE:
540                         temp = 0;
541
542                         if (wParam & MK_LBUTTON)
543                                 temp |= 1;
544
545                         if (wParam & MK_RBUTTON)
546                                 temp |= 2;
547
548                         if (wParam & MK_MBUTTON)
549                                 temp |= 4;
550
551                         IN_MouseEvent (temp);
552
553                         break;
554
555                 // JACK: This is the mouse wheel with the Intellimouse
556                 // Its delta is either positive or neg, and we generate the proper
557                 // Event.
558                 case WM_MOUSEWHEEL:
559                         if ((short) HIWORD(wParam) > 0) {
560                                 Key_Event(K_MWHEELUP, true);
561                                 Key_Event(K_MWHEELUP, false);
562                         } else {
563                                 Key_Event(K_MWHEELDOWN, true);
564                                 Key_Event(K_MWHEELDOWN, false);
565                         }
566                         break;
567
568                 case WM_SIZE:
569                         break;
570
571                 case WM_CLOSE:
572                         if (MessageBox (mainwindow, "Are you sure you want to quit?", "Confirm Exit", MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES)
573                                 Sys_Quit ();
574
575                         break;
576
577                 case WM_ACTIVATE:
578                         fActive = LOWORD(wParam);
579                         fMinimized = (BOOL) HIWORD(wParam);
580                         AppActivate(!(fActive == WA_INACTIVE), fMinimized);
581
582                 // fix the leftover Alt from any Alt-Tab or the like that switched us away
583                         ClearAllStates ();
584
585                         break;
586
587                 //case WM_DESTROY:
588                 //      PostQuitMessage (0);
589                 //      break;
590
591                 case MM_MCINOTIFY:
592                         lRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam);
593                         break;
594
595                 default:
596                         /* pass all unhandled messages to DefWindowProc */
597                         lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
598                 break;
599         }
600
601         /* return 1 if handled message, 0 if not */
602         return lRet;
603 }
604
605 int VID_SetGamma(unsigned short *ramps)
606 {
607         HDC hdc = GetDC (NULL);
608         int i = SetDeviceGammaRamp(hdc, ramps);
609         ReleaseDC (NULL, hdc);
610         return i; // return success or failure
611 }
612
613 int VID_GetGamma(unsigned short *ramps)
614 {
615         HDC hdc = GetDC (NULL);
616         int i = GetDeviceGammaRamp(hdc, ramps);
617         ReleaseDC (NULL, hdc);
618         return i; // return success or failure
619 }
620
621 static HINSTANCE gldll;
622
623 int GL_OpenLibrary(const char *name)
624 {
625         Con_Printf("Loading GL driver %s\n", name);
626         GL_CloseLibrary();
627         if (!(gldll = LoadLibrary(name)))
628         {
629                 Con_Printf("Unable to LoadLibrary %s\n", name);
630                 return false;
631         }
632         strcpy(gl_driver, name);
633         return true;
634 }
635
636 void GL_CloseLibrary(void)
637 {
638         FreeLibrary(gldll);
639         gldll = 0;
640         gl_driver[0] = 0;
641         qwglGetProcAddress = NULL;
642         gl_extensions = "";
643         gl_platform = "";
644         gl_platformextensions = "";
645 }
646
647 void *GL_GetProcAddress(const char *name)
648 {
649         void *p = NULL;
650         if (qwglGetProcAddress != NULL)
651                 p = (void *) qwglGetProcAddress(name);
652         if (p == NULL)
653                 p = (void *) GetProcAddress(gldll, name);
654         return p;
655 }
656
657 static void IN_Init(void);
658 void VID_Init(void)
659 {
660         WNDCLASS wc;
661
662         InitCommonControls();
663         hIcon = LoadIcon (global_hInstance, MAKEINTRESOURCE (IDI_ICON2));
664
665         // Register the frame class
666         wc.style         = 0;
667         wc.lpfnWndProc   = (WNDPROC)MainWndProc;
668         wc.cbClsExtra    = 0;
669         wc.cbWndExtra    = 0;
670         wc.hInstance     = global_hInstance;
671         wc.hIcon         = 0;
672         wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
673         wc.hbrBackground = NULL;
674         wc.lpszMenuName  = 0;
675         wc.lpszClassName = "DarkPlacesWindowClass";
676
677         if (!RegisterClass (&wc))
678                 Sys_Error("Couldn't register window class\n");
679
680         IN_Init();
681 }
682
683 int VID_InitMode (int fullscreen, int width, int height, int bpp, int stencil)
684 {
685         int i;
686         HDC hdc;
687         RECT rect;
688         MSG msg;
689         PIXELFORMATDESCRIPTOR pfd =
690         {
691                 sizeof(PIXELFORMATDESCRIPTOR),  // size of this pfd
692                 1,                              // version number
693                 PFD_DRAW_TO_WINDOW              // support window
694                 |  PFD_SUPPORT_OPENGL   // support OpenGL
695                 |  PFD_DOUBLEBUFFER ,   // double buffered
696                 PFD_TYPE_RGBA,                  // RGBA type
697                 24,                             // 24-bit color depth
698                 0, 0, 0, 0, 0, 0,               // color bits ignored
699                 0,                              // no alpha buffer
700                 0,                              // shift bit ignored
701                 0,                              // no accumulation buffer
702                 0, 0, 0, 0,                     // accum bits ignored
703                 32,                             // 32-bit z-buffer
704                 0,                              // no stencil buffer
705                 0,                              // no auxiliary buffer
706                 PFD_MAIN_PLANE,                 // main layer
707                 0,                              // reserved
708                 0, 0, 0                         // layer masks ignored
709         };
710         int pixelformat;
711         DWORD WindowStyle, ExWindowStyle;
712         HGLRC baseRC;
713         int CenterX, CenterY;
714         const char *gldrivername;
715
716         if (vid_initialized)
717                 Sys_Error("VID_InitMode called when video is already initialised\n");
718
719         // if stencil is enabled, ask for alpha too
720         if (stencil)
721         {
722                 pfd.cStencilBits = 8;
723                 pfd.cAlphaBits = 8;
724         }
725         else
726         {
727                 pfd.cStencilBits = 0;
728                 pfd.cAlphaBits = 0;
729         }
730
731         gldrivername = "opengl32.dll";
732         i = COM_CheckParm("-gl_driver");
733         if (i && i < com_argc - 1)
734                 gldrivername = com_argv[i + 1];
735         if (!GL_OpenLibrary(gldrivername))
736         {
737                 Con_Printf("Unable to load GL driver %s\n", gldrivername);
738                 return false;
739         }
740
741         memset(&gdevmode, 0, sizeof(gdevmode));
742
743         vid_isfullscreen = false;
744         if (fullscreen)
745         {
746                 gdevmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
747                 gdevmode.dmBitsPerPel = bpp;
748                 gdevmode.dmPelsWidth = width;
749                 gdevmode.dmPelsHeight = height;
750                 gdevmode.dmSize = sizeof (gdevmode);
751                 if (ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
752                 {
753                         VID_Shutdown();
754                         Con_Printf("Unable to change to requested mode %dx%dx%dbpp\n", width, height, bpp);
755                         return false;
756                 }
757
758                 vid_isfullscreen = true;
759                 WindowStyle = WS_POPUP;
760                 ExWindowStyle = 0;
761         }
762         else
763         {
764                 hdc = GetDC (NULL);
765                 i = GetDeviceCaps(hdc, RASTERCAPS);
766                 ReleaseDC (NULL, hdc);
767                 if (i & RC_PALETTE)
768                 {
769                         VID_Shutdown();
770                         Con_Printf ("Can't run in non-RGB mode\n");
771                         return false;
772                 }
773
774                 WindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
775                 ExWindowStyle = 0;
776         }
777
778         rect.top = 0;
779         rect.left = 0;
780         rect.right = width;
781         rect.bottom = height;
782         AdjustWindowRectEx(&rect, WindowStyle, false, 0);
783
784         if (fullscreen)
785         {
786                 CenterX = 0;
787                 CenterY = 0;
788         }
789         else
790         {
791                 CenterX = (GetSystemMetrics(SM_CXSCREEN) - (rect.right - rect.left)) / 2;
792                 CenterY = (GetSystemMetrics(SM_CYSCREEN) - (rect.bottom - rect.top)) / 2;
793         }
794         CenterX = max(0, CenterX);
795         CenterY = max(0, CenterY);
796
797         // x and y may be changed by WM_MOVE messages
798         window_x = CenterX;
799         window_y = CenterY;
800         window_width = width;
801         window_height = height;
802         rect.left += CenterX;
803         rect.right += CenterX;
804         rect.top += CenterY;
805         rect.bottom += CenterY;
806
807         mainwindow = CreateWindowEx (ExWindowStyle, "DarkPlacesWindowClass", gamename, WindowStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, global_hInstance, NULL);
808         if (!mainwindow)
809         {
810                 Con_Printf("CreateWindowEx(%d, %s, %s, %d, %d, %d, %d, %d, %p, %p, %d, %p) failed\n", ExWindowStyle, "DarkPlacesWindowClass", gamename, WindowStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, global_hInstance, NULL);
811                 VID_Shutdown();
812                 return false;
813         }
814
815         /*
816         if (!fullscreen)
817                 SetWindowPos (mainwindow, NULL, CenterX, CenterY, 0, 0,SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME);
818         */
819
820         ShowWindow (mainwindow, SW_SHOWDEFAULT);
821         UpdateWindow (mainwindow);
822
823         SendMessage (mainwindow, WM_SETICON, (WPARAM)true, (LPARAM)hIcon);
824         SendMessage (mainwindow, WM_SETICON, (WPARAM)false, (LPARAM)hIcon);
825
826         VID_UpdateWindowStatus ();
827
828         // now we try to make sure we get the focus on the mode switch, because
829         // sometimes in some systems we don't.  We grab the foreground, then
830         // finish setting up, pump all our messages, and sleep for a little while
831         // to let messages finish bouncing around the system, then we put
832         // ourselves at the top of the z order, then grab the foreground again,
833         // Who knows if it helps, but it probably doesn't hurt
834         SetForegroundWindow (mainwindow);
835
836         while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
837         {
838                 TranslateMessage (&msg);
839                 DispatchMessage (&msg);
840         }
841
842         Sleep (100);
843
844         SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOCOPYBITS);
845
846         SetForegroundWindow (mainwindow);
847
848         // fix the leftover Alt from any Alt-Tab or the like that switched us away
849         ClearAllStates ();
850
851         hdc = GetDC(mainwindow);
852
853         if ((pixelformat = ChoosePixelFormat(hdc, &pfd)) == 0)
854         {
855                 VID_Shutdown();
856                 Con_Printf("ChoosePixelFormat(%d, %p) failed\n", hdc, &pfd);
857                 return false;
858         }
859
860         if (SetPixelFormat(hdc, pixelformat, &pfd) == false)
861         {
862                 VID_Shutdown();
863                 Con_Printf("SetPixelFormat(%d, %d, %p) failed\n", hdc, pixelformat, &pfd);
864                 return false;
865         }
866
867         if (!GL_CheckExtension("wgl", wglfuncs, NULL, false))
868         {
869                 VID_Shutdown();
870                 Con_Printf("wgl functions not found\n");
871                 return false;
872         }
873
874         baseRC = qwglCreateContext(hdc);
875         if (!baseRC)
876         {
877                 VID_Shutdown();
878                 Con_Printf("Could not initialize GL (wglCreateContext failed).\n\nMake sure you are in 65536 color mode, and try running -window.\n");
879                 return false;
880         }
881         if (!qwglMakeCurrent(hdc, baseRC))
882         {
883                 VID_Shutdown();
884                 Con_Printf("wglMakeCurrent(%d, %d) failed\n", hdc, baseRC);
885                 return false;
886         }
887
888         qglGetString = GL_GetProcAddress("glGetString");
889         qwglGetExtensionsStringARB = GL_GetProcAddress("wglGetExtensionsStringARB");
890         if (qglGetString == NULL)
891         {
892                 VID_Shutdown();
893                 Con_Printf("glGetString not found\n");
894                 return false;
895         }
896         gl_renderer = qglGetString(GL_RENDERER);
897         gl_vendor = qglGetString(GL_VENDOR);
898         gl_version = qglGetString(GL_VERSION);
899         gl_extensions = qglGetString(GL_EXTENSIONS);
900         gl_platform = "WGL";
901         gl_platformextensions = "";
902
903         if (qwglGetExtensionsStringARB)
904                 gl_platformextensions = qwglGetExtensionsStringARB(hdc);
905
906         gl_videosyncavailable = GL_CheckExtension("WGL_EXT_swap_control", wglswapintervalfuncs, NULL, false);
907         ReleaseDC(mainwindow, hdc);
908
909         GL_Init ();
910
911         // LordHavoc: special differences for ATI (broken 8bit color when also using 32bit? weird!)
912         if (strncasecmp(gl_vendor,"ATI",3)==0)
913         {
914                 if (strncasecmp(gl_renderer,"Rage Pro",8)==0)
915                         isRagePro = true;
916         }
917         if (strncasecmp(gl_renderer,"Matrox G200 Direct3D",20)==0) // a D3D driver for GL? sigh...
918                 isG200 = true;
919
920         //vid_menudrawfn = VID_MenuDraw;
921         //vid_menukeyfn = VID_MenuKey;
922         vid_hidden = false;
923         vid_initialized = true;
924
925         IN_StartupMouse ();
926         IN_StartupJoystick ();
927
928         return true;
929 }
930
931 static void IN_Shutdown(void);
932 void VID_Shutdown (void)
933 {
934         HGLRC hRC = 0;
935         HDC hDC = 0;
936
937         VID_RestoreSystemGamma();
938
939         vid_initialized = false;
940         IN_Shutdown();
941         if (qwglGetCurrentContext)
942                 hRC = qwglGetCurrentContext();
943         if (qwglGetCurrentDC)
944                 hDC = qwglGetCurrentDC();
945         if (qwglMakeCurrent)
946                 qwglMakeCurrent(NULL, NULL);
947         if (hRC && qwglDeleteContext)
948                 qwglDeleteContext(hRC);
949         // close the library before we get rid of the window
950         GL_CloseLibrary();
951         if (hDC && mainwindow)
952                 ReleaseDC(mainwindow, hDC);
953         if (vid_isfullscreen)
954                 ChangeDisplaySettings (NULL, 0);
955         vid_isfullscreen = false;
956         AppActivate(false, false);
957         if (mainwindow)
958                 DestroyWindow(mainwindow);
959         mainwindow = 0;
960 }
961
962
963 /*
964 ===========
965 IN_ShowMouse
966 ===========
967 */
968 void IN_ShowMouse (void)
969 {
970         if (!mouseshowtoggle)
971         {
972                 ShowCursor (true);
973                 mouseshowtoggle = 1;
974         }
975 }
976
977
978 /*
979 ===========
980 IN_HideMouse
981 ===========
982 */
983 void IN_HideMouse (void)
984 {
985         if (mouseshowtoggle)
986         {
987                 ShowCursor (false);
988                 mouseshowtoggle = 0;
989         }
990 }
991
992
993 /*
994 ===========
995 IN_ActivateMouse
996 ===========
997 */
998 void IN_ActivateMouse (void)
999 {
1000
1001         mouseactivatetoggle = true;
1002
1003         if (mouseinitialized)
1004         {
1005                 if (dinput)
1006                 {
1007                         if (g_pMouse)
1008                         {
1009                                 if (!dinput_acquired)
1010                                 {
1011                                         IDirectInputDevice_Acquire(g_pMouse);
1012                                         dinput_acquired = true;
1013                                 }
1014                         }
1015                         else
1016                         {
1017                                 return;
1018                         }
1019                 }
1020                 else
1021                 {
1022                         RECT window_rect;
1023                         window_rect.left = window_x;
1024                         window_rect.top = window_y;
1025                         window_rect.right = window_x + window_width;
1026                         window_rect.bottom = window_y + window_height;
1027
1028                         if (mouseparmsvalid)
1029                                 restore_spi = SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0);
1030
1031                         SetCursorPos (window_center_x, window_center_y);
1032                         SetCapture (mainwindow);
1033                         ClipCursor (&window_rect);
1034                 }
1035
1036                 mouseactive = true;
1037         }
1038 }
1039
1040
1041 /*
1042 ===========
1043 IN_DeactivateMouse
1044 ===========
1045 */
1046 void IN_DeactivateMouse (void)
1047 {
1048
1049         mouseactivatetoggle = false;
1050
1051         if (mouseinitialized)
1052         {
1053                 if (dinput)
1054                 {
1055                         if (g_pMouse)
1056                         {
1057                                 if (dinput_acquired)
1058                                 {
1059                                         IDirectInputDevice_Unacquire(g_pMouse);
1060                                         dinput_acquired = false;
1061                                 }
1062                         }
1063                 }
1064                 else
1065                 {
1066                         if (restore_spi)
1067                                 SystemParametersInfo (SPI_SETMOUSE, 0, originalmouseparms, 0);
1068
1069                         ClipCursor (NULL);
1070                         ReleaseCapture ();
1071                 }
1072
1073                 mouseactive = false;
1074         }
1075 }
1076
1077
1078 /*
1079 ===========
1080 IN_InitDInput
1081 ===========
1082 */
1083 qboolean IN_InitDInput (void)
1084 {
1085     HRESULT             hr;
1086         DIPROPDWORD     dipdw = {
1087                 {
1088                         sizeof(DIPROPDWORD),        // diph.dwSize
1089                         sizeof(DIPROPHEADER),       // diph.dwHeaderSize
1090                         0,                          // diph.dwObj
1091                         DIPH_DEVICE,                // diph.dwHow
1092                 },
1093                 DINPUT_BUFFERSIZE,              // dwData
1094         };
1095
1096         if (!hInstDI)
1097         {
1098                 hInstDI = LoadLibrary("dinput.dll");
1099
1100                 if (hInstDI == NULL)
1101                 {
1102                         Con_SafePrintf ("Couldn't load dinput.dll\n");
1103                         return false;
1104                 }
1105         }
1106
1107         if (!pDirectInputCreate)
1108         {
1109                 pDirectInputCreate = (void *)GetProcAddress(hInstDI,"DirectInputCreateA");
1110
1111                 if (!pDirectInputCreate)
1112                 {
1113                         Con_SafePrintf ("Couldn't get DI proc addr\n");
1114                         return false;
1115                 }
1116         }
1117
1118 // register with DirectInput and get an IDirectInput to play with.
1119         hr = iDirectInputCreate(global_hInstance, DIRECTINPUT_VERSION, &g_pdi, NULL);
1120
1121         if (FAILED(hr))
1122         {
1123                 return false;
1124         }
1125
1126 // obtain an interface to the system mouse device.
1127         hr = IDirectInput_CreateDevice(g_pdi, &GUID_SysMouse, &g_pMouse, NULL);
1128
1129         if (FAILED(hr))
1130         {
1131                 Con_SafePrintf ("Couldn't open DI mouse device\n");
1132                 return false;
1133         }
1134
1135 // set the data format to "mouse format".
1136         hr = IDirectInputDevice_SetDataFormat(g_pMouse, &df);
1137
1138         if (FAILED(hr))
1139         {
1140                 Con_SafePrintf ("Couldn't set DI mouse format\n");
1141                 return false;
1142         }
1143
1144 // set the cooperativity level.
1145         hr = IDirectInputDevice_SetCooperativeLevel(g_pMouse, mainwindow,
1146                         DISCL_EXCLUSIVE | DISCL_FOREGROUND);
1147
1148         if (FAILED(hr))
1149         {
1150                 Con_SafePrintf ("Couldn't set DI coop level\n");
1151                 return false;
1152         }
1153
1154
1155 // set the buffer size to DINPUT_BUFFERSIZE elements.
1156 // the buffer size is a DWORD property associated with the device
1157         hr = IDirectInputDevice_SetProperty(g_pMouse, DIPROP_BUFFERSIZE, &dipdw.diph);
1158
1159         if (FAILED(hr))
1160         {
1161                 Con_SafePrintf ("Couldn't set DI buffersize\n");
1162                 return false;
1163         }
1164
1165         return true;
1166 }
1167
1168
1169 /*
1170 ===========
1171 IN_StartupMouse
1172 ===========
1173 */
1174 void IN_StartupMouse (void)
1175 {
1176         if (COM_CheckParm ("-nomouse") || COM_CheckParm("-safe"))
1177                 return;
1178
1179         mouseinitialized = true;
1180
1181         if (COM_CheckParm ("-dinput"))
1182         {
1183                 dinput = IN_InitDInput ();
1184
1185                 if (dinput)
1186                 {
1187                         Con_SafePrintf ("DirectInput initialized\n");
1188                 }
1189                 else
1190                 {
1191                         Con_SafePrintf ("DirectInput not initialized\n");
1192                 }
1193         }
1194
1195         if (!dinput)
1196         {
1197                 mouseparmsvalid = SystemParametersInfo (SPI_GETMOUSE, 0, originalmouseparms, 0);
1198
1199                 if (mouseparmsvalid)
1200                 {
1201                         if ( COM_CheckParm ("-noforcemspd") )
1202                                 newmouseparms[2] = originalmouseparms[2];
1203
1204                         if ( COM_CheckParm ("-noforcemaccel") )
1205                         {
1206                                 newmouseparms[0] = originalmouseparms[0];
1207                                 newmouseparms[1] = originalmouseparms[1];
1208                         }
1209
1210                         if ( COM_CheckParm ("-noforcemparms") )
1211                         {
1212                                 newmouseparms[0] = originalmouseparms[0];
1213                                 newmouseparms[1] = originalmouseparms[1];
1214                                 newmouseparms[2] = originalmouseparms[2];
1215                         }
1216                 }
1217         }
1218
1219         mouse_buttons = 3;
1220
1221 // if a fullscreen video mode was set before the mouse was initialized,
1222 // set the mouse state appropriately
1223         if (mouseactivatetoggle)
1224                 IN_ActivateMouse ();
1225 }
1226
1227
1228 /*
1229 ===========
1230 IN_MouseEvent
1231 ===========
1232 */
1233 void IN_MouseEvent (int mstate)
1234 {
1235         int     i;
1236
1237         if (mouseactive && !dinput)
1238         {
1239         // perform button actions
1240                 for (i=0 ; i<mouse_buttons ; i++)
1241                 {
1242                         if ( (mstate & (1<<i)) &&
1243                                 !(mouse_oldbuttonstate & (1<<i)) )
1244                         {
1245                                 Key_Event (K_MOUSE1 + i, true);
1246                         }
1247
1248                         if ( !(mstate & (1<<i)) &&
1249                                 (mouse_oldbuttonstate & (1<<i)) )
1250                         {
1251                                 Key_Event (K_MOUSE1 + i, false);
1252                         }
1253                 }
1254
1255                 mouse_oldbuttonstate = mstate;
1256         }
1257 }
1258
1259
1260 /*
1261 ===========
1262 IN_MouseMove
1263 ===========
1264 */
1265 void IN_MouseMove (usercmd_t *cmd)
1266 {
1267         int                                     i, mx, my;
1268         DIDEVICEOBJECTDATA      od;
1269         DWORD                           dwElements;
1270         HRESULT                         hr;
1271
1272         if (!mouseactive)
1273         {
1274                 GetCursorPos (&current_pos);
1275                 ui_mouseupdate(current_pos.x - window_x, current_pos.y - window_y);
1276                 return;
1277         }
1278
1279         if (dinput)
1280         {
1281                 mx = 0;
1282                 my = 0;
1283
1284                 for (;;)
1285                 {
1286                         dwElements = 1;
1287
1288                         hr = IDirectInputDevice_GetDeviceData(g_pMouse,
1289                                         sizeof(DIDEVICEOBJECTDATA), &od, &dwElements, 0);
1290
1291                         if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED))
1292                         {
1293                                 dinput_acquired = true;
1294                                 IDirectInputDevice_Acquire(g_pMouse);
1295                                 break;
1296                         }
1297
1298                         /* Unable to read data or no data available */
1299                         if (FAILED(hr) || dwElements == 0)
1300                                 break;
1301
1302                         /* Look at the element to see what happened */
1303
1304                         switch (od.dwOfs)
1305                         {
1306                                 case DIMOFS_X:
1307                                         mx += od.dwData;
1308                                         break;
1309
1310                                 case DIMOFS_Y:
1311                                         my += od.dwData;
1312                                         break;
1313
1314                                 case DIMOFS_BUTTON0:
1315                                         if (od.dwData & 0x80)
1316                                                 mstate_di |= 1;
1317                                         else
1318                                                 mstate_di &= ~1;
1319                                         break;
1320
1321                                 case DIMOFS_BUTTON1:
1322                                         if (od.dwData & 0x80)
1323                                                 mstate_di |= (1<<1);
1324                                         else
1325                                                 mstate_di &= ~(1<<1);
1326                                         break;
1327
1328                                 case DIMOFS_BUTTON2:
1329                                         if (od.dwData & 0x80)
1330                                                 mstate_di |= (1<<2);
1331                                         else
1332                                                 mstate_di &= ~(1<<2);
1333                                         break;
1334                         }
1335                 }
1336
1337         // perform button actions
1338                 for (i=0 ; i<mouse_buttons ; i++)
1339                 {
1340                         if ( (mstate_di & (1<<i)) &&
1341                                 !(mouse_oldbuttonstate & (1<<i)) )
1342                         {
1343                                 Key_Event (K_MOUSE1 + i, true);
1344                         }
1345
1346                         if ( !(mstate_di & (1<<i)) &&
1347                                 (mouse_oldbuttonstate & (1<<i)) )
1348                         {
1349                                 Key_Event (K_MOUSE1 + i, false);
1350                         }
1351                 }
1352
1353                 mouse_oldbuttonstate = mstate_di;
1354         }
1355         else
1356         {
1357                 GetCursorPos (&current_pos);
1358                 mx = current_pos.x - window_center_x + mx_accum;
1359                 my = current_pos.y - window_center_y + my_accum;
1360                 mx_accum = 0;
1361                 my_accum = 0;
1362         }
1363
1364         IN_Mouse(cmd, mx, my);
1365
1366         // if the mouse has moved, force it to the center, so there's room to move
1367         if (!dinput && (mx || my))
1368                 SetCursorPos (window_center_x, window_center_y);
1369 }
1370
1371
1372 /*
1373 ===========
1374 IN_Move
1375 ===========
1376 */
1377 void IN_Move (usercmd_t *cmd)
1378 {
1379         if (vid_activewindow && !vid_hidden)
1380         {
1381                 IN_MouseMove (cmd);
1382                 IN_JoyMove (cmd);
1383         }
1384 }
1385
1386
1387 /*
1388 ===========
1389 IN_Accumulate
1390 ===========
1391 */
1392 void IN_Accumulate (void)
1393 {
1394         if (mouseactive)
1395         {
1396                 if (!dinput)
1397                 {
1398                         GetCursorPos (&current_pos);
1399
1400                         mx_accum += current_pos.x - window_center_x;
1401                         my_accum += current_pos.y - window_center_y;
1402
1403                 // force the mouse to the center, so there's room to move
1404                         SetCursorPos (window_center_x, window_center_y);
1405                 }
1406         }
1407 }
1408
1409
1410 /*
1411 ===================
1412 IN_ClearStates
1413 ===================
1414 */
1415 void IN_ClearStates (void)
1416 {
1417         if (mouseactive)
1418         {
1419                 mx_accum = 0;
1420                 my_accum = 0;
1421                 mouse_oldbuttonstate = 0;
1422         }
1423 }
1424
1425
1426 /*
1427 ===============
1428 IN_StartupJoystick
1429 ===============
1430 */
1431 void IN_StartupJoystick (void)
1432 {
1433         int                     numdevs;
1434         JOYCAPS         jc;
1435         MMRESULT        mmr;
1436         mmr = 0;
1437
1438         // assume no joystick
1439         joy_avail = false;
1440
1441         // abort startup if user requests no joystick
1442         if (COM_CheckParm ("-nojoy") || COM_CheckParm("-safe"))
1443                 return;
1444
1445         // verify joystick driver is present
1446         if ((numdevs = joyGetNumDevs ()) == 0)
1447         {
1448                 Con_Printf ("\njoystick not found -- driver not present\n\n");
1449                 return;
1450         }
1451
1452         // cycle through the joystick ids for the first valid one
1453         for (joy_id=0 ; joy_id<numdevs ; joy_id++)
1454         {
1455                 memset (&ji, 0, sizeof(ji));
1456                 ji.dwSize = sizeof(ji);
1457                 ji.dwFlags = JOY_RETURNCENTERED;
1458
1459                 if ((mmr = joyGetPosEx (joy_id, &ji)) == JOYERR_NOERROR)
1460                         break;
1461         }
1462
1463         // abort startup if we didn't find a valid joystick
1464         if (mmr != JOYERR_NOERROR)
1465         {
1466                 Con_Printf ("\njoystick not found -- no valid joysticks (%x)\n\n", mmr);
1467                 return;
1468         }
1469
1470         // get the capabilities of the selected joystick
1471         // abort startup if command fails
1472         memset (&jc, 0, sizeof(jc));
1473         if ((mmr = joyGetDevCaps (joy_id, &jc, sizeof(jc))) != JOYERR_NOERROR)
1474         {
1475                 Con_Printf ("\njoystick not found -- invalid joystick capabilities (%x)\n\n", mmr);
1476                 return;
1477         }
1478
1479         // save the joystick's number of buttons and POV status
1480         joy_numbuttons = jc.wNumButtons;
1481         joy_haspov = jc.wCaps & JOYCAPS_HASPOV;
1482
1483         // old button and POV states default to no buttons pressed
1484         joy_oldbuttonstate = joy_oldpovstate = 0;
1485
1486         // mark the joystick as available and advanced initialization not completed
1487         // this is needed as cvars are not available during initialization
1488
1489         joy_avail = true;
1490         joy_advancedinit = false;
1491
1492         Con_Printf ("\njoystick detected\n\n");
1493 }
1494
1495
1496 /*
1497 ===========
1498 RawValuePointer
1499 ===========
1500 */
1501 PDWORD RawValuePointer (int axis)
1502 {
1503         switch (axis)
1504         {
1505         case JOY_AXIS_X:
1506                 return &ji.dwXpos;
1507         case JOY_AXIS_Y:
1508                 return &ji.dwYpos;
1509         case JOY_AXIS_Z:
1510                 return &ji.dwZpos;
1511         case JOY_AXIS_R:
1512                 return &ji.dwRpos;
1513         case JOY_AXIS_U:
1514                 return &ji.dwUpos;
1515         case JOY_AXIS_V:
1516                 return &ji.dwVpos;
1517         }
1518         return NULL; // LordHavoc: hush compiler warning
1519 }
1520
1521
1522 /*
1523 ===========
1524 Joy_AdvancedUpdate_f
1525 ===========
1526 */
1527 void Joy_AdvancedUpdate_f (void)
1528 {
1529
1530         // called once by IN_ReadJoystick and by user whenever an update is needed
1531         // cvars are now available
1532         int     i;
1533         DWORD dwTemp;
1534
1535         // initialize all the maps
1536         for (i = 0; i < JOY_MAX_AXES; i++)
1537         {
1538                 dwAxisMap[i] = AxisNada;
1539                 dwControlMap[i] = JOY_ABSOLUTE_AXIS;
1540                 pdwRawValue[i] = RawValuePointer(i);
1541         }
1542
1543         if( joy_advanced.integer == 0)
1544         {
1545                 // default joystick initialization
1546                 // 2 axes only with joystick control
1547                 dwAxisMap[JOY_AXIS_X] = AxisTurn;
1548                 // dwControlMap[JOY_AXIS_X] = JOY_ABSOLUTE_AXIS;
1549                 dwAxisMap[JOY_AXIS_Y] = AxisForward;
1550                 // dwControlMap[JOY_AXIS_Y] = JOY_ABSOLUTE_AXIS;
1551         }
1552         else
1553         {
1554                 if (strcmp (joy_name.string, "joystick") != 0)
1555                 {
1556                         // notify user of advanced controller
1557                         Con_Printf ("\n%s configured\n\n", joy_name.string);
1558                 }
1559
1560                 // advanced initialization here
1561                 // data supplied by user via joy_axisn cvars
1562                 dwTemp = (DWORD) joy_advaxisx.value;
1563                 dwAxisMap[JOY_AXIS_X] = dwTemp & 0x0000000f;
1564                 dwControlMap[JOY_AXIS_X] = dwTemp & JOY_RELATIVE_AXIS;
1565                 dwTemp = (DWORD) joy_advaxisy.value;
1566                 dwAxisMap[JOY_AXIS_Y] = dwTemp & 0x0000000f;
1567                 dwControlMap[JOY_AXIS_Y] = dwTemp & JOY_RELATIVE_AXIS;
1568                 dwTemp = (DWORD) joy_advaxisz.value;
1569                 dwAxisMap[JOY_AXIS_Z] = dwTemp & 0x0000000f;
1570                 dwControlMap[JOY_AXIS_Z] = dwTemp & JOY_RELATIVE_AXIS;
1571                 dwTemp = (DWORD) joy_advaxisr.value;
1572                 dwAxisMap[JOY_AXIS_R] = dwTemp & 0x0000000f;
1573                 dwControlMap[JOY_AXIS_R] = dwTemp & JOY_RELATIVE_AXIS;
1574                 dwTemp = (DWORD) joy_advaxisu.value;
1575                 dwAxisMap[JOY_AXIS_U] = dwTemp & 0x0000000f;
1576                 dwControlMap[JOY_AXIS_U] = dwTemp & JOY_RELATIVE_AXIS;
1577                 dwTemp = (DWORD) joy_advaxisv.value;
1578                 dwAxisMap[JOY_AXIS_V] = dwTemp & 0x0000000f;
1579                 dwControlMap[JOY_AXIS_V] = dwTemp & JOY_RELATIVE_AXIS;
1580         }
1581
1582         // compute the axes to collect from DirectInput
1583         joy_flags = JOY_RETURNCENTERED | JOY_RETURNBUTTONS | JOY_RETURNPOV;
1584         for (i = 0; i < JOY_MAX_AXES; i++)
1585         {
1586                 if (dwAxisMap[i] != AxisNada)
1587                 {
1588                         joy_flags |= dwAxisFlags[i];
1589                 }
1590         }
1591 }
1592
1593
1594 /*
1595 ===========
1596 IN_Commands
1597 ===========
1598 */
1599 void IN_Commands (void)
1600 {
1601         int             i, key_index;
1602         DWORD   buttonstate, povstate;
1603
1604         if (!joy_avail)
1605         {
1606                 return;
1607         }
1608
1609
1610         // loop through the joystick buttons
1611         // key a joystick event or auxillary event for higher number buttons for each state change
1612         buttonstate = ji.dwButtons;
1613         for (i=0 ; i < (int) joy_numbuttons ; i++)
1614         {
1615                 if ( (buttonstate & (1<<i)) && !(joy_oldbuttonstate & (1<<i)) )
1616                 {
1617                         key_index = (i < 4) ? K_JOY1 : K_AUX1;
1618                         Key_Event (key_index + i, true);
1619                 }
1620
1621                 if ( !(buttonstate & (1<<i)) && (joy_oldbuttonstate & (1<<i)) )
1622                 {
1623                         key_index = (i < 4) ? K_JOY1 : K_AUX1;
1624                         Key_Event (key_index + i, false);
1625                 }
1626         }
1627         joy_oldbuttonstate = buttonstate;
1628
1629         if (joy_haspov)
1630         {
1631                 // convert POV information into 4 bits of state information
1632                 // this avoids any potential problems related to moving from one
1633                 // direction to another without going through the center position
1634                 povstate = 0;
1635                 if(ji.dwPOV != JOY_POVCENTERED)
1636                 {
1637                         if (ji.dwPOV == JOY_POVFORWARD)
1638                                 povstate |= 0x01;
1639                         if (ji.dwPOV == JOY_POVRIGHT)
1640                                 povstate |= 0x02;
1641                         if (ji.dwPOV == JOY_POVBACKWARD)
1642                                 povstate |= 0x04;
1643                         if (ji.dwPOV == JOY_POVLEFT)
1644                                 povstate |= 0x08;
1645                 }
1646                 // determine which bits have changed and key an auxillary event for each change
1647                 for (i=0 ; i < 4 ; i++)
1648                 {
1649                         if ( (povstate & (1<<i)) && !(joy_oldpovstate & (1<<i)) )
1650                         {
1651                                 Key_Event (K_AUX29 + i, true);
1652                         }
1653
1654                         if ( !(povstate & (1<<i)) && (joy_oldpovstate & (1<<i)) )
1655                         {
1656                                 Key_Event (K_AUX29 + i, false);
1657                         }
1658                 }
1659                 joy_oldpovstate = povstate;
1660         }
1661 }
1662
1663
1664 /*
1665 ===============
1666 IN_ReadJoystick
1667 ===============
1668 */
1669 qboolean IN_ReadJoystick (void)
1670 {
1671
1672         memset (&ji, 0, sizeof(ji));
1673         ji.dwSize = sizeof(ji);
1674         ji.dwFlags = joy_flags;
1675
1676         if (joyGetPosEx (joy_id, &ji) == JOYERR_NOERROR)
1677         {
1678                 // this is a hack -- there is a bug in the Logitech WingMan Warrior DirectInput Driver
1679                 // rather than having 32768 be the zero point, they have the zero point at 32668
1680                 // go figure -- anyway, now we get the full resolution out of the device
1681                 if (joy_wwhack1.integer != 0.0)
1682                 {
1683                         ji.dwUpos += 100;
1684                 }
1685                 return true;
1686         }
1687         else
1688         {
1689                 // read error occurred
1690                 // turning off the joystick seems too harsh for 1 read error,
1691                 // but what should be done?
1692                 return false;
1693         }
1694 }
1695
1696
1697 /*
1698 ===========
1699 IN_JoyMove
1700 ===========
1701 */
1702 void IN_JoyMove (usercmd_t *cmd)
1703 {
1704         float   speed, aspeed;
1705         float   fAxisValue, fTemp;
1706         int             i, mouselook = (in_mlook.state & 1) || freelook.integer;
1707
1708         // complete initialization if first time in
1709         // this is needed as cvars are not available at initialization time
1710         if( joy_advancedinit != true )
1711         {
1712                 Joy_AdvancedUpdate_f();
1713                 joy_advancedinit = true;
1714         }
1715
1716         // verify joystick is available and that the user wants to use it
1717         if (!joy_avail || !in_joystick.integer)
1718         {
1719                 return;
1720         }
1721
1722         // collect the joystick data, if possible
1723         if (IN_ReadJoystick () != true)
1724         {
1725                 return;
1726         }
1727
1728         if (in_speed.state & 1)
1729                 speed = cl_movespeedkey.value;
1730         else
1731                 speed = 1;
1732         // LordHavoc: viewzoom affects sensitivity for sniping
1733         aspeed = speed * host_realframetime * cl.viewzoom;
1734
1735         // loop through the axes
1736         for (i = 0; i < JOY_MAX_AXES; i++)
1737         {
1738                 // get the floating point zero-centered, potentially-inverted data for the current axis
1739                 fAxisValue = (float) *pdwRawValue[i];
1740                 // move centerpoint to zero
1741                 fAxisValue -= 32768.0;
1742
1743                 if (joy_wwhack2.integer != 0.0)
1744                 {
1745                         if (dwAxisMap[i] == AxisTurn)
1746                         {
1747                                 // this is a special formula for the Logitech WingMan Warrior
1748                                 // y=ax^b; where a = 300 and b = 1.3
1749                                 // also x values are in increments of 800 (so this is factored out)
1750                                 // then bounds check result to level out excessively high spin rates
1751                                 fTemp = 300.0 * pow(abs(fAxisValue) / 800.0, 1.3);
1752                                 if (fTemp > 14000.0)
1753                                         fTemp = 14000.0;
1754                                 // restore direction information
1755                                 fAxisValue = (fAxisValue > 0.0) ? fTemp : -fTemp;
1756                         }
1757                 }
1758
1759                 // convert range from -32768..32767 to -1..1
1760                 fAxisValue /= 32768.0;
1761
1762                 switch (dwAxisMap[i])
1763                 {
1764                 case AxisForward:
1765                         if ((joy_advanced.integer == 0) && mouselook)
1766                         {
1767                                 // user wants forward control to become look control
1768                                 if (fabs(fAxisValue) > joy_pitchthreshold.value)
1769                                 {
1770                                         // if mouse invert is on, invert the joystick pitch value
1771                                         // only absolute control support here (joy_advanced is false)
1772                                         if (m_pitch.value < 0.0)
1773                                         {
1774                                                 cl.viewangles[PITCH] -= (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value;
1775                                         }
1776                                         else
1777                                         {
1778                                                 cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value;
1779                                         }
1780                                         V_StopPitchDrift();
1781                                 }
1782                                 else
1783                                 {
1784                                         // no pitch movement
1785                                         // disable pitch return-to-center unless requested by user
1786                                         // *** this code can be removed when the lookspring bug is fixed
1787                                         // *** the bug always has the lookspring feature on
1788                                         if(lookspring.value == 0.0)
1789                                                 V_StopPitchDrift();
1790                                 }
1791                         }
1792                         else
1793                         {
1794                                 // user wants forward control to be forward control
1795                                 if (fabs(fAxisValue) > joy_forwardthreshold.value)
1796                                 {
1797                                         cmd->forwardmove += (fAxisValue * joy_forwardsensitivity.value) * speed * cl_forwardspeed.value;
1798                                 }
1799                         }
1800                         break;
1801
1802                 case AxisSide:
1803                         if (fabs(fAxisValue) > joy_sidethreshold.value)
1804                         {
1805                                 cmd->sidemove += (fAxisValue * joy_sidesensitivity.value) * speed * cl_sidespeed.value;
1806                         }
1807                         break;
1808
1809                 case AxisTurn:
1810                         if ((in_strafe.state & 1) || (lookstrafe.integer && mouselook))
1811                         {
1812                                 // user wants turn control to become side control
1813                                 if (fabs(fAxisValue) > joy_sidethreshold.value)
1814                                 {
1815                                         cmd->sidemove -= (fAxisValue * joy_sidesensitivity.value) * speed * cl_sidespeed.value;
1816                                 }
1817                         }
1818                         else
1819                         {
1820                                 // user wants turn control to be turn control
1821                                 if (fabs(fAxisValue) > joy_yawthreshold.value)
1822                                 {
1823                                         if(dwControlMap[i] == JOY_ABSOLUTE_AXIS)
1824                                         {
1825                                                 cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity.value) * aspeed * cl_yawspeed.value;
1826                                         }
1827                                         else
1828                                         {
1829                                                 cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity.value) * speed * 180.0;
1830                                         }
1831
1832                                 }
1833                         }
1834                         break;
1835
1836                 case AxisLook:
1837                         if (mouselook)
1838                         {
1839                                 if (fabs(fAxisValue) > joy_pitchthreshold.value)
1840                                 {
1841                                         // pitch movement detected and pitch movement desired by user
1842                                         if(dwControlMap[i] == JOY_ABSOLUTE_AXIS)
1843                                         {
1844                                                 cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value;
1845                                         }
1846                                         else
1847                                         {
1848                                                 cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * speed * 180.0;
1849                                         }
1850                                         V_StopPitchDrift();
1851                                 }
1852                                 else
1853                                 {
1854                                         // no pitch movement
1855                                         // disable pitch return-to-center unless requested by user
1856                                         // *** this code can be removed when the lookspring bug is fixed
1857                                         // *** the bug always has the lookspring feature on
1858                                         if(lookspring.integer == 0)
1859                                                 V_StopPitchDrift();
1860                                 }
1861                         }
1862                         break;
1863
1864                 default:
1865                         break;
1866                 }
1867         }
1868 }
1869
1870 static void IN_Init(void)
1871 {
1872         uiWheelMessage = RegisterWindowMessage ( "MSWHEEL_ROLLMSG" );
1873
1874         // joystick variables
1875         Cvar_RegisterVariable (&in_joystick);
1876         Cvar_RegisterVariable (&joy_name);
1877         Cvar_RegisterVariable (&joy_advanced);
1878         Cvar_RegisterVariable (&joy_advaxisx);
1879         Cvar_RegisterVariable (&joy_advaxisy);
1880         Cvar_RegisterVariable (&joy_advaxisz);
1881         Cvar_RegisterVariable (&joy_advaxisr);
1882         Cvar_RegisterVariable (&joy_advaxisu);
1883         Cvar_RegisterVariable (&joy_advaxisv);
1884         Cvar_RegisterVariable (&joy_forwardthreshold);
1885         Cvar_RegisterVariable (&joy_sidethreshold);
1886         Cvar_RegisterVariable (&joy_pitchthreshold);
1887         Cvar_RegisterVariable (&joy_yawthreshold);
1888         Cvar_RegisterVariable (&joy_forwardsensitivity);
1889         Cvar_RegisterVariable (&joy_sidesensitivity);
1890         Cvar_RegisterVariable (&joy_pitchsensitivity);
1891         Cvar_RegisterVariable (&joy_yawsensitivity);
1892         Cvar_RegisterVariable (&joy_wwhack1);
1893         Cvar_RegisterVariable (&joy_wwhack2);
1894         Cmd_AddCommand ("joyadvancedupdate", Joy_AdvancedUpdate_f);
1895 }
1896
1897 static void IN_Shutdown(void)
1898 {
1899         IN_DeactivateMouse ();
1900         IN_ShowMouse ();
1901
1902         if (g_pMouse)
1903                 IDirectInputDevice_Release(g_pMouse);
1904         g_pMouse = NULL;
1905
1906         if (g_pdi)
1907                 IDirectInput_Release(g_pdi);
1908         g_pdi = NULL;
1909 }