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