]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - vid_wgl.c
2d0ac602f5865c1fd2c3daa30a50a05c9ae3ded0
[xonotic/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 // vid_wgl.c -- NT GL vid component
21
22 #ifdef _MSC_VER
23 #pragma comment(lib, "comctl32.lib")
24 #endif
25
26 #ifdef SUPPORTDIRECTX
27 // Include DX libs
28 #ifdef _MSC_VER
29 #pragma comment(lib, "dinput8.lib")
30 #pragma comment(lib, "dxguid.lib")
31 #endif
32 #ifndef DIRECTINPUT_VERSION
33 #       define DIRECTINPUT_VERSION 0x0500  /* Version 5.0 */
34 #endif
35 #endif
36
37 #include "quakedef.h"
38 #include <windows.h>
39 #include <mmsystem.h>
40 #ifdef SUPPORTDIRECTX
41 #include <dsound.h>
42 #endif
43 #include "resource.h"
44 #include <commctrl.h>
45 #ifdef SUPPORTDIRECTX
46 #include <dinput.h>
47 #endif
48 #include "dpsoftrast.h"
49
50 #ifdef SUPPORTD3D
51 #include <d3d9.h>
52
53 cvar_t vid_dx9 = {CVAR_SAVE, "vid_dx9", "0", "use Microsoft Direct3D9(r) for rendering"};
54 cvar_t vid_dx9_hal = {CVAR_SAVE, "vid_dx9_hal", "1", "enables hardware rendering (1), otherwise software reference rasterizer (0 - very slow), note that 0 is necessary when using NVPerfHUD (which renders in hardware but requires this option to enable it)"};
55 cvar_t vid_dx9_softvertex = {CVAR_SAVE, "vid_dx9_softvertex", "0", "enables software vertex processing (for compatibility testing?  or if you have a very fast CPU), usually you want this off"};
56 cvar_t vid_dx9_triplebuffer = {CVAR_SAVE, "vid_dx9_triplebuffer", "0", "enables triple buffering when using vid_vsync in fullscreen, this options adds some latency and only helps when framerate is below 60 so you usually don't want it"};
57 //cvar_t vid_dx10 = {CVAR_SAVE, "vid_dx10", "1", "use Microsoft Direct3D10(r) for rendering"};
58 //cvar_t vid_dx11 = {CVAR_SAVE, "vid_dx11", "1", "use Microsoft Direct3D11(r) for rendering"};
59
60 D3DPRESENT_PARAMETERS vid_d3dpresentparameters;
61
62 // we declare this in vid_shared.c because it is required by dedicated server and all clients when SUPPORTD3D is defined
63 extern LPDIRECT3DDEVICE9 vid_d3d9dev;
64
65 LPDIRECT3D9 vid_d3d9;
66 D3DCAPS9 vid_d3d9caps;
67 qboolean vid_d3ddevicelost;
68 #endif
69
70 extern HINSTANCE global_hInstance;
71
72 static HINSTANCE gldll;
73
74 #ifndef WM_MOUSEWHEEL
75 #define WM_MOUSEWHEEL                   0x020A
76 #endif
77
78 // Tell startup code that we have a client
79 int cl_available = true;
80
81 qboolean vid_supportrefreshrate = true;
82
83 static int (WINAPI *qwglChoosePixelFormat)(HDC, CONST PIXELFORMATDESCRIPTOR *);
84 static int (WINAPI *qwglDescribePixelFormat)(HDC, int, UINT, LPPIXELFORMATDESCRIPTOR);
85 //static int (WINAPI *qwglGetPixelFormat)(HDC);
86 static BOOL (WINAPI *qwglSetPixelFormat)(HDC, int, CONST PIXELFORMATDESCRIPTOR *);
87 static BOOL (WINAPI *qwglSwapBuffers)(HDC);
88 static HGLRC (WINAPI *qwglCreateContext)(HDC);
89 static BOOL (WINAPI *qwglDeleteContext)(HGLRC);
90 static HGLRC (WINAPI *qwglGetCurrentContext)(VOID);
91 static HDC (WINAPI *qwglGetCurrentDC)(VOID);
92 static PROC (WINAPI *qwglGetProcAddress)(LPCSTR);
93 static BOOL (WINAPI *qwglMakeCurrent)(HDC, HGLRC);
94 static BOOL (WINAPI *qwglSwapIntervalEXT)(int interval);
95 static const char *(WINAPI *qwglGetExtensionsStringARB)(HDC hdc);
96 static BOOL (WINAPI *qwglChoosePixelFormatARB)(HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
97 static BOOL (WINAPI *qwglGetPixelFormatAttribivARB)(HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, int *piValues);
98
99 static dllfunction_t wglfuncs[] =
100 {
101         {"wglChoosePixelFormat", (void **) &qwglChoosePixelFormat},
102         {"wglDescribePixelFormat", (void **) &qwglDescribePixelFormat},
103 //      {"wglGetPixelFormat", (void **) &qwglGetPixelFormat},
104         {"wglSetPixelFormat", (void **) &qwglSetPixelFormat},
105         {"wglSwapBuffers", (void **) &qwglSwapBuffers},
106         {"wglCreateContext", (void **) &qwglCreateContext},
107         {"wglDeleteContext", (void **) &qwglDeleteContext},
108         {"wglGetProcAddress", (void **) &qwglGetProcAddress},
109         {"wglMakeCurrent", (void **) &qwglMakeCurrent},
110         {"wglGetCurrentContext", (void **) &qwglGetCurrentContext},
111         {"wglGetCurrentDC", (void **) &qwglGetCurrentDC},
112         {NULL, NULL}
113 };
114
115 static dllfunction_t wglswapintervalfuncs[] =
116 {
117         {"wglSwapIntervalEXT", (void **) &qwglSwapIntervalEXT},
118         {NULL, NULL}
119 };
120
121 static dllfunction_t wglpixelformatfuncs[] =
122 {
123         {"wglChoosePixelFormatARB", (void **) &qwglChoosePixelFormatARB},
124         {"wglGetPixelFormatAttribivARB", (void **) &qwglGetPixelFormatAttribivARB},
125         {NULL, NULL}
126 };
127
128 static DEVMODE gdevmode, initialdevmode;
129 static qboolean vid_initialized = false;
130 static qboolean vid_wassuspended = false;
131 static qboolean vid_usingmouse = false;
132 static qboolean vid_usinghidecursor = false;
133 static qboolean vid_usingvsync = false;
134 static qboolean vid_usevsync = false;
135 static HICON hIcon;
136
137 // used by cd_win.c and snd_win.c
138 HWND mainwindow;
139
140 static HDC       baseDC;
141 static HGLRC baseRC;
142
143 static HDC vid_softhdc;
144 static HGDIOBJ vid_softhdc_backup;
145 static BITMAPINFO vid_softbmi;
146 static HBITMAP vid_softdibhandle;
147
148 //HWND WINAPI InitializeWindow (HINSTANCE hInstance, int nCmdShow);
149
150 static qboolean vid_isfullscreen;
151
152 //void VID_MenuDraw (void);
153 //void VID_MenuKey (int key);
154
155 //LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
156 //void AppActivate(BOOL fActive, BOOL minimize);
157 //void ClearAllStates (void);
158 //void VID_UpdateWindowStatus (void);
159
160 //====================================
161
162 static int window_x, window_y;
163
164 static qboolean mouseinitialized;
165
166 #ifdef SUPPORTDIRECTX
167 static qboolean dinput;
168 #define DINPUT_BUFFERSIZE           16
169 #define iDirectInputCreate(a,b,c,d)     pDirectInputCreate(a,b,c,d)
170
171 static HRESULT (WINAPI *pDirectInputCreate)(HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUT * lplpDirectInput, LPUNKNOWN punkOuter);
172 #endif
173
174 // LordHavoc: thanks to backslash for this support for mouse buttons 4 and 5
175 /* backslash :: imouse explorer buttons */
176 /* These are #ifdefed out for non-Win2K in the February 2001 version of
177    MS's platform SDK, but we need them for compilation. . . */
178 #ifndef WM_XBUTTONDOWN
179    #define WM_XBUTTONDOWN      0x020B
180    #define WM_XBUTTONUP      0x020C
181 #endif
182 #ifndef MK_XBUTTON1
183    #define MK_XBUTTON1         0x0020
184    #define MK_XBUTTON2         0x0040
185 #endif
186 #ifndef MK_XBUTTON3
187 // LordHavoc: lets hope this allows more buttons in the future...
188    #define MK_XBUTTON3         0x0080
189    #define MK_XBUTTON4         0x0100
190    #define MK_XBUTTON5         0x0200
191    #define MK_XBUTTON6         0x0400
192    #define MK_XBUTTON7         0x0800
193 #endif
194 /* :: backslash */
195
196 // mouse variables
197 static int                      mouse_buttons;
198 static int                      mouse_oldbuttonstate;
199
200 static unsigned int uiWheelMessage;
201 #ifdef SUPPORTDIRECTX
202 static qboolean dinput_acquired;
203
204 static unsigned int             mstate_di;
205 #endif
206
207 // joystick defines and variables
208 // where should defines be moved?
209 #define JOY_ABSOLUTE_AXIS       0x00000000              // control like a joystick
210 #define JOY_RELATIVE_AXIS       0x00000010              // control like a mouse, spinner, trackball
211 #define JOY_MAX_AXES            6                               // X, Y, Z, R, U, V
212 #define JOY_AXIS_X                      0
213 #define JOY_AXIS_Y                      1
214 #define JOY_AXIS_Z                      2
215 #define JOY_AXIS_R                      3
216 #define JOY_AXIS_U                      4
217 #define JOY_AXIS_V                      5
218
219 // joystick axes state
220 typedef struct
221 {
222         float oldmove;
223         float move;
224         float mdelta;
225         double keytime;
226 }joy_axiscache_t;
227 static joy_axiscache_t joy_axescache[JOY_MAX_AXES];
228
229 enum _ControlList
230 {
231         AxisNada = 0, AxisForward, AxisLook, AxisSide, AxisTurn
232 };
233
234 static DWORD    dwAxisFlags[JOY_MAX_AXES] =
235 {
236         JOY_RETURNX, JOY_RETURNY, JOY_RETURNZ, JOY_RETURNR, JOY_RETURNU, JOY_RETURNV
237 };
238
239 static DWORD    dwAxisMap[JOY_MAX_AXES];
240 static DWORD    dwControlMap[JOY_MAX_AXES];
241 static PDWORD   pdwRawValue[JOY_MAX_AXES];
242
243 // none of these cvars are saved over a session
244 // this means that advanced controller configuration needs to be executed
245 // each time.  this avoids any problems with getting back to a default usage
246 // or when changing from one controller to another.  this way at least something
247 // works.
248 static cvar_t in_joystick = {CVAR_SAVE, "joystick","0", "enables joysticks"};
249 static cvar_t joy_name = {0, "joyname", "joystick", "name of joystick to use (informational only, used only by joyadvanced 1 mode)"};
250 static cvar_t joy_advanced = {0, "joyadvanced", "0", "use more than 2 axis joysticks (configuring this is very technical)"};
251 static cvar_t joy_advaxisx = {0, "joyadvaxisx", "0", "axis mapping for joyadvanced 1 mode"};
252 static cvar_t joy_advaxisy = {0, "joyadvaxisy", "0", "axis mapping for joyadvanced 1 mode"};
253 static cvar_t joy_advaxisz = {0, "joyadvaxisz", "0", "axis mapping for joyadvanced 1 mode"};
254 static cvar_t joy_advaxisr = {0, "joyadvaxisr", "0", "axis mapping for joyadvanced 1 mode"};
255 static cvar_t joy_advaxisu = {0, "joyadvaxisu", "0", "axis mapping for joyadvanced 1 mode"};
256 static cvar_t joy_advaxisv = {0, "joyadvaxisv", "0", "axis mapping for joyadvanced 1 mode"};
257 static cvar_t joy_forwardthreshold = {0, "joyforwardthreshold", "0.15", "minimum joystick movement necessary to move forward"};
258 static cvar_t joy_sidethreshold = {0, "joysidethreshold", "0.15", "minimum joystick movement necessary to move sideways (strafing)"};
259 static cvar_t joy_pitchthreshold = {0, "joypitchthreshold", "0.15", "minimum joystick movement necessary to look up/down"};
260 static cvar_t joy_yawthreshold = {0, "joyyawthreshold", "0.15", "minimum joystick movement necessary to turn left/right"};
261 static cvar_t joy_forwardsensitivity = {0, "joyforwardsensitivity", "-1.0", "how fast the joystick moves forward"};
262 static cvar_t joy_sidesensitivity = {0, "joysidesensitivity", "-1.0", "how fast the joystick moves sideways (strafing)"};
263 static cvar_t joy_pitchsensitivity = {0, "joypitchsensitivity", "1.0", "how fast the joystick looks up/down"};
264 static cvar_t joy_yawsensitivity = {0, "joyyawsensitivity", "-1.0", "how fast the joystick turns left/right"};
265 static cvar_t joy_wwhack1 = {0, "joywwhack1", "0.0", "special hack for wingman warrior"};
266 static cvar_t joy_wwhack2 = {0, "joywwhack2", "0.0", "special hack for wingman warrior"};
267 static cvar_t joy_axiskeyevents = {CVAR_SAVE, "joy_axiskeyevents", "0", "generate uparrow/leftarrow etc. keyevents for joystick axes, use if your joystick driver is not generating them"};
268 static cvar_t joy_axiskeyevents_deadzone = {CVAR_SAVE, "joy_axiskeyevents_deadzone", "0.5", "deadzone value for axes"};
269
270 static cvar_t vid_forcerefreshrate = {0, "vid_forcerefreshrate", "0", "try to set the given vid_refreshrate even if Windows doesn't list it as valid video mode"};
271
272 static qboolean joy_avail, joy_advancedinit, joy_haspov;
273 static DWORD            joy_oldbuttonstate, joy_oldpovstate;
274
275 static int                      joy_id;
276 static DWORD            joy_flags;
277 static DWORD            joy_numbuttons;
278
279 #ifdef SUPPORTDIRECTX
280 static LPDIRECTINPUT            g_pdi;
281 static LPDIRECTINPUTDEVICE      g_pMouse;
282 static HINSTANCE hInstDI;
283 #endif
284
285 static JOYINFOEX        ji;
286
287 // forward-referenced functions
288 static void IN_StartupJoystick (void);
289 static void Joy_AdvancedUpdate_f (void);
290 static void IN_JoyMove (void);
291 static void IN_StartupMouse (void);
292
293
294 //====================================
295
296 qboolean vid_reallyhidden = true;
297 #ifdef SUPPORTD3D
298 qboolean vid_begunscene = false;
299 #endif
300 void VID_Finish (void)
301 {
302 #ifdef SUPPORTD3D
303         HRESULT hr;
304 #endif
305         vid_hidden = vid_reallyhidden;
306
307         vid_usevsync = vid_vsync.integer && !cls.timedemo && qwglSwapIntervalEXT;
308
309         if (!vid_hidden)
310         {
311                 switch(vid.renderpath)
312                 {
313                 case RENDERPATH_GL11:
314                 case RENDERPATH_GL13:
315                 case RENDERPATH_GL20:
316                 case RENDERPATH_GLES1:
317                 case RENDERPATH_GLES2:
318                         if (vid_usingvsync != vid_usevsync)
319                         {
320                                 vid_usingvsync = vid_usevsync;
321                                 qwglSwapIntervalEXT (vid_usevsync);
322                         }
323                         if (r_speeds.integer == 2 || gl_finish.integer)
324                                 GL_Finish();
325                         SwapBuffers(baseDC);
326                         break;
327                 case RENDERPATH_D3D9:
328 #ifdef SUPPORTD3D
329                         if (vid_begunscene)
330                         {
331                                 IDirect3DDevice9_EndScene(vid_d3d9dev);
332                                 vid_begunscene = false;
333                         }
334                         if (!vid_reallyhidden)
335                         {
336                                 if (!vid_d3ddevicelost)
337                                 {
338                                         vid_hidden = vid_reallyhidden;
339                                         hr = IDirect3DDevice9_Present(vid_d3d9dev, NULL, NULL, NULL, NULL);
340                                         if (hr == D3DERR_DEVICELOST)
341                                         {
342                                                 vid_d3ddevicelost = true;
343                                                 vid_hidden = true;
344                                                 Sleep(100);
345                                         }
346                                 }
347                                 else
348                                 {
349                                         hr = IDirect3DDevice9_TestCooperativeLevel(vid_d3d9dev);
350                                         switch(hr)
351                                         {
352                                         case D3DERR_DEVICELOST:
353                                                 vid_d3ddevicelost = true;
354                                                 vid_hidden = true;
355                                                 Sleep(100);
356                                                 break;
357                                         case D3DERR_DEVICENOTRESET:
358                                                 vid_d3ddevicelost = false;
359                                                 vid_hidden = vid_reallyhidden;
360                                                 R_Modules_DeviceLost();
361                                                 IDirect3DDevice9_Reset(vid_d3d9dev, &vid_d3dpresentparameters);
362                                                 R_Modules_DeviceRestored();
363                                                 break;
364                                         case D3D_OK:
365                                                 vid_hidden = vid_reallyhidden;
366                                                 IDirect3DDevice9_Present(vid_d3d9dev, NULL, NULL, NULL, NULL);
367                                                 break;
368                                         }
369                                 }
370                                 if (!vid_begunscene && !vid_hidden)
371                                 {
372                                         IDirect3DDevice9_BeginScene(vid_d3d9dev);
373                                         vid_begunscene = true;
374                                 }
375                         }
376 #endif
377                         break;
378                 case RENDERPATH_D3D10:
379                         break;
380                 case RENDERPATH_D3D11:
381                         break;
382                 case RENDERPATH_SOFT:
383                         DPSOFTRAST_Finish();
384 //                      baseDC = GetDC(mainwindow);
385                         BitBlt(baseDC, 0, 0, vid.width, vid.height, vid_softhdc, 0, 0, SRCCOPY);
386 //                      ReleaseDC(mainwindow, baseDC);
387 //                      baseDC = NULL;
388                         break;
389                 }
390         }
391
392         // make sure a context switch can happen every frame - Logitech drivers
393         // input drivers sometimes eat cpu time every 3 seconds or lag badly
394         // without this help
395         Sleep(0);
396
397         VID_UpdateGamma(false, 256);
398 }
399
400 //==========================================================================
401
402
403
404
405 static unsigned char scantokey[128] =
406 {
407 //  0           1       2    3     4     5       6       7      8         9      A          B           C       D           E           F
408         0          ,27    ,'1'  ,'2'  ,'3'  ,'4'    ,'5'    ,'6'   ,'7'      ,'8'   ,'9'       ,'0'        ,'-'   ,'='         ,K_BACKSPACE,9    ,//0
409         'q'        ,'w'   ,'e'  ,'r'  ,'t'  ,'y'    ,'u'    ,'i'   ,'o'      ,'p'   ,'['       ,']'        ,13    ,K_CTRL      ,'a'        ,'s'  ,//1
410         'd'        ,'f'   ,'g'  ,'h'  ,'j'  ,'k'    ,'l'    ,';'   ,'\''     ,'`'   ,K_SHIFT   ,'\\'       ,'z'   ,'x'         ,'c'        ,'v'  ,//2
411         'b'        ,'n'   ,'m'  ,','  ,'.'  ,'/'    ,K_SHIFT,'*'   ,K_ALT    ,' '   ,0         ,K_F1       ,K_F2  ,K_F3        ,K_F4       ,K_F5 ,//3
412         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
413         K_DOWNARROW,K_PGDN,K_INS,K_DEL,0    ,0      ,0      ,K_F11 ,K_F12    ,0     ,0         ,0          ,0     ,0           ,0          ,0    ,//5
414         0          ,0     ,0    ,0    ,0    ,0      ,0      ,0     ,0        ,0     ,0         ,0          ,0     ,0           ,0          ,0    ,//6
415         0          ,0     ,0    ,0    ,0    ,0      ,0      ,0     ,0        ,0     ,0         ,0          ,0     ,0           ,0          ,0     //7
416 };
417
418
419 /*
420 =======
421 MapKey
422
423 Map from windows to quake keynums
424 =======
425 */
426 static int MapKey (int key, int virtualkey)
427 {
428         int result;
429         int modified = (key >> 16) & 255;
430         qboolean is_extended = false;
431
432         if (modified < 128 && scantokey[modified])
433                 result = scantokey[modified];
434         else
435         {
436                 result = 0;
437                 Con_DPrintf("key 0x%02x (0x%8x, 0x%8x) has no translation\n", modified, key, virtualkey);
438         }
439
440         if (key & (1 << 24))
441                 is_extended = true;
442
443         if ( !is_extended )
444         {
445                 switch ( result )
446                 {
447                 case K_HOME:
448                         return K_KP_HOME;
449                 case K_UPARROW:
450                         return K_KP_UPARROW;
451                 case K_PGUP:
452                         return K_KP_PGUP;
453                 case K_LEFTARROW:
454                         return K_KP_LEFTARROW;
455                 case K_RIGHTARROW:
456                         return K_KP_RIGHTARROW;
457                 case K_END:
458                         return K_KP_END;
459                 case K_DOWNARROW:
460                         return K_KP_DOWNARROW;
461                 case K_PGDN:
462                         return K_KP_PGDN;
463                 case K_INS:
464                         return K_KP_INS;
465                 case K_DEL:
466                         return K_KP_DEL;
467                 default:
468                         return result;
469                 }
470         }
471         else
472         {
473                 switch ( result )
474                 {
475                 case 0x0D:
476                         return K_KP_ENTER;
477                 case 0x2F:
478                         return K_KP_SLASH;
479                 case 0xAF:
480                         return K_KP_PLUS;
481                 }
482                 return result;
483         }
484 }
485
486 /*
487 ===================================================================
488
489 MAIN WINDOW
490
491 ===================================================================
492 */
493
494 /*
495 ================
496 ClearAllStates
497 ================
498 */
499 static void ClearAllStates (void)
500 {
501         Key_ClearStates ();
502         if (vid_usingmouse)
503                 mouse_oldbuttonstate = 0;
504 }
505
506 void AppActivate(BOOL fActive, BOOL minimize)
507 /****************************************************************************
508 *
509 * Function:     AppActivate
510 * Parameters:   fActive - True if app is activating
511 *
512 * Description:  If the application is activating, then swap the system
513 *               into SYSPAL_NOSTATIC mode so that our palettes will display
514 *               correctly.
515 *
516 ****************************************************************************/
517 {
518         static qboolean sound_active = false;  // initially blocked by Sys_InitConsole()
519
520         vid_activewindow = fActive != FALSE;
521         vid_reallyhidden = minimize != FALSE;
522
523         // enable/disable sound on focus gain/loss
524         if ((!vid_reallyhidden && vid_activewindow) || !snd_mutewhenidle.integer)
525         {
526                 if (!sound_active)
527                 {
528                         S_UnblockSound ();
529                         sound_active = true;
530                 }
531         }
532         else
533         {
534                 if (sound_active)
535                 {
536                         S_BlockSound ();
537                         sound_active = false;
538                 }
539         }
540
541         if (fActive)
542         {
543                 if (vid_isfullscreen)
544                 {
545                         if (vid_wassuspended)
546                         {
547                                 vid_wassuspended = false;
548                                 if (gldll)
549                                 {
550                                         ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN);
551                                         ShowWindow(mainwindow, SW_SHOWNORMAL);
552                                 }
553                         }
554
555                         // LordHavoc: from dabb, fix for alt-tab bug in NVidia drivers
556                         if (gldll)
557                                 MoveWindow(mainwindow,0,0,gdevmode.dmPelsWidth,gdevmode.dmPelsHeight,false);
558                 }
559         }
560
561         if (!fActive)
562         {
563                 VID_SetMouse(false, false, false);
564                 if (vid_isfullscreen)
565                 {
566                         if (gldll)
567                                 ChangeDisplaySettings (NULL, 0);
568                         vid_wassuspended = true;
569                 }
570                 VID_RestoreSystemGamma();
571         }
572 }
573
574 //TODO: move it around in vid_wgl.c since I dont think this is the right position
575 void Sys_SendKeyEvents (void)
576 {
577         MSG msg;
578
579         while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
580         {
581                 if (!GetMessage (&msg, NULL, 0, 0))
582                         Sys_Quit (1);
583
584                 TranslateMessage (&msg);
585                 DispatchMessage (&msg);
586         }
587 }
588
589 LONG CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
590
591 static keynum_t buttonremap[16] =
592 {
593         K_MOUSE1,
594         K_MOUSE2,
595         K_MOUSE3,
596         K_MOUSE4,
597         K_MOUSE5,
598         K_MOUSE6,
599         K_MOUSE7,
600         K_MOUSE8,
601         K_MOUSE9,
602         K_MOUSE10,
603         K_MOUSE11,
604         K_MOUSE12,
605         K_MOUSE13,
606         K_MOUSE14,
607         K_MOUSE15,
608         K_MOUSE16,
609 };
610
611 /* main window procedure */
612 static qboolean IN_JoystickBlockDoubledKeyEvents(int keycode);
613 LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM  wParam, LPARAM lParam)
614 {
615         LONG    lRet = 1;
616         int             fActive, fMinimized, temp;
617         unsigned char state[256];
618         unsigned char asciichar[4];
619         int             vkey;
620         int             charlength;
621         qboolean down = false;
622
623         if ( uMsg == uiWheelMessage )
624                 uMsg = WM_MOUSEWHEEL;
625
626         switch (uMsg)
627         {
628                 case WM_KILLFOCUS:
629                         if (vid_isfullscreen)
630                                 ShowWindow(mainwindow, SW_SHOWMINNOACTIVE);
631                         break;
632
633                 case WM_CREATE:
634                         break;
635
636                 case WM_MOVE:
637                         window_x = (int) LOWORD(lParam);
638                         window_y = (int) HIWORD(lParam);
639                         VID_SetMouse(false, false, false);
640                         break;
641
642                 case WM_KEYDOWN:
643                 case WM_SYSKEYDOWN:
644                         down = true;
645                 case WM_KEYUP:
646                 case WM_SYSKEYUP:
647                         vkey = MapKey(lParam, wParam);
648                         GetKeyboardState (state);
649                         // alt/ctrl/shift tend to produce funky ToAscii values,
650                         // and if it's not a single character we don't know care about it
651                         charlength = ToAscii (wParam, lParam >> 16, state, (LPWORD)asciichar, 0);
652                         if (vkey == K_ALT || vkey == K_CTRL || vkey == K_SHIFT || charlength == 0)
653                                 asciichar[0] = 0;
654                         else if( charlength == 2 ) {
655                                 asciichar[0] = asciichar[1];
656                         }
657                         if (!IN_JoystickBlockDoubledKeyEvents(vkey))
658                                 Key_Event (vkey, asciichar[0], down);
659                         break;
660
661                 case WM_SYSCHAR:
662                 // keep Alt-Space from happening
663                         break;
664
665                 case WM_SYSCOMMAND:
666                         // prevent screensaver from occuring while the active window
667                         // note: password-locked screensavers on Vista still work
668                         if (vid_activewindow && ((wParam & 0xFFF0) == SC_SCREENSAVE || (wParam & 0xFFF0) == SC_MONITORPOWER))
669                                 lRet = 0;
670                         else
671                                 lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
672                         break;
673
674         // this is complicated because Win32 seems to pack multiple mouse events into
675         // one update sometimes, so we always check all states and look for events
676                 case WM_LBUTTONDOWN:
677                 case WM_LBUTTONUP:
678                 case WM_RBUTTONDOWN:
679                 case WM_RBUTTONUP:
680                 case WM_MBUTTONDOWN:
681                 case WM_MBUTTONUP:
682                 case WM_XBUTTONDOWN:   // backslash :: imouse explorer buttons
683                 case WM_XBUTTONUP:      // backslash :: imouse explorer buttons
684                 case WM_MOUSEMOVE:
685                         temp = 0;
686
687                         if (wParam & MK_LBUTTON)
688                                 temp |= 1;
689
690                         if (wParam & MK_RBUTTON)
691                                 temp |= 2;
692
693                         if (wParam & MK_MBUTTON)
694                                 temp |= 4;
695
696                         /* backslash :: imouse explorer buttons */
697                         if (wParam & MK_XBUTTON1)
698                                 temp |= 8;
699
700                         if (wParam & MK_XBUTTON2)
701                                 temp |= 16;
702                         /* :: backslash */
703
704                         // LordHavoc: lets hope this allows more buttons in the future...
705                         if (wParam & MK_XBUTTON3)
706                                 temp |= 32;
707                         if (wParam & MK_XBUTTON4)
708                                 temp |= 64;
709                         if (wParam & MK_XBUTTON5)
710                                 temp |= 128;
711                         if (wParam & MK_XBUTTON6)
712                                 temp |= 256;
713                         if (wParam & MK_XBUTTON7)
714                                 temp |= 512;
715
716 #ifdef SUPPORTDIRECTX
717                         if (!dinput_acquired)
718 #endif
719                         {
720                                 // perform button actions
721                                 int i;
722                                 for (i=0 ; i<mouse_buttons && i < 16 ; i++)
723                                         if ((temp ^ mouse_oldbuttonstate) & (1<<i))
724                                                 Key_Event (buttonremap[i], 0, (temp & (1<<i)) != 0);
725                                 mouse_oldbuttonstate = temp;
726                         }
727
728                         break;
729
730                 // JACK: This is the mouse wheel with the Intellimouse
731                 // Its delta is either positive or neg, and we generate the proper
732                 // Event.
733                 case WM_MOUSEWHEEL:
734                         if ((short) HIWORD(wParam) > 0) {
735                                 Key_Event(K_MWHEELUP, 0, true);
736                                 Key_Event(K_MWHEELUP, 0, false);
737                         } else {
738                                 Key_Event(K_MWHEELDOWN, 0, true);
739                                 Key_Event(K_MWHEELDOWN, 0, false);
740                         }
741                         break;
742
743                 case WM_SIZE:
744                         break;
745
746                 case WM_CLOSE:
747                         if (MessageBox (mainwindow, "Are you sure you want to quit?", "Confirm Exit", MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES)
748                                 Sys_Quit (0);
749
750                         break;
751
752                 case WM_ACTIVATE:
753                         fActive = LOWORD(wParam);
754                         fMinimized = (BOOL) HIWORD(wParam);
755                         AppActivate(!(fActive == WA_INACTIVE), fMinimized);
756
757                 // fix the leftover Alt from any Alt-Tab or the like that switched us away
758                         ClearAllStates ();
759
760                         break;
761
762                 //case WM_DESTROY:
763                 //      PostQuitMessage (0);
764                 //      break;
765
766                 case MM_MCINOTIFY:
767                         lRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam);
768                         break;
769
770                 default:
771                         /* pass all unhandled messages to DefWindowProc */
772                         lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
773                 break;
774         }
775
776         /* return 1 if handled message, 0 if not */
777         return lRet;
778 }
779
780 int VID_SetGamma(unsigned short *ramps, int rampsize)
781 {
782         if (qwglMakeCurrent)
783         {
784                 HDC hdc = GetDC (NULL);
785                 int i = SetDeviceGammaRamp(hdc, ramps);
786                 ReleaseDC (NULL, hdc);
787                 return i; // return success or failure
788         }
789         else
790                 return 0;
791 }
792
793 int VID_GetGamma(unsigned short *ramps, int rampsize)
794 {
795         if (qwglMakeCurrent)
796         {
797                 HDC hdc = GetDC (NULL);
798                 int i = GetDeviceGammaRamp(hdc, ramps);
799                 ReleaseDC (NULL, hdc);
800                 return i; // return success or failure
801         }
802         else
803                 return 0;
804 }
805
806 static void GL_CloseLibrary(void)
807 {
808         if (gldll)
809         {
810                 FreeLibrary(gldll);
811                 gldll = 0;
812                 gl_driver[0] = 0;
813                 qwglGetProcAddress = NULL;
814                 gl_extensions = "";
815                 gl_platform = "";
816                 gl_platformextensions = "";
817         }
818 }
819
820 static int GL_OpenLibrary(const char *name)
821 {
822         Con_Printf("Loading OpenGL driver %s\n", name);
823         GL_CloseLibrary();
824         if (!(gldll = LoadLibrary(name)))
825         {
826                 Con_Printf("Unable to LoadLibrary %s\n", name);
827                 return false;
828         }
829         strlcpy(gl_driver, name, sizeof(gl_driver));
830         return true;
831 }
832
833 void *GL_GetProcAddress(const char *name)
834 {
835         if (gldll)
836         {
837                 void *p = NULL;
838                 if (qwglGetProcAddress != NULL)
839                         p = (void *) qwglGetProcAddress(name);
840                 if (p == NULL)
841                         p = (void *) GetProcAddress(gldll, name);
842                 return p;
843         }
844         else
845                 return NULL;
846 }
847
848 #ifndef WGL_ARB_pixel_format
849 #define WGL_NUMBER_PIXEL_FORMATS_ARB   0x2000
850 #define WGL_DRAW_TO_WINDOW_ARB         0x2001
851 #define WGL_DRAW_TO_BITMAP_ARB         0x2002
852 #define WGL_ACCELERATION_ARB           0x2003
853 #define WGL_NEED_PALETTE_ARB           0x2004
854 #define WGL_NEED_SYSTEM_PALETTE_ARB    0x2005
855 #define WGL_SWAP_LAYER_BUFFERS_ARB     0x2006
856 #define WGL_SWAP_METHOD_ARB            0x2007
857 #define WGL_NUMBER_OVERLAYS_ARB        0x2008
858 #define WGL_NUMBER_UNDERLAYS_ARB       0x2009
859 #define WGL_TRANSPARENT_ARB            0x200A
860 #define WGL_TRANSPARENT_RED_VALUE_ARB  0x2037
861 #define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038
862 #define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039
863 #define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A
864 #define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B
865 #define WGL_SHARE_DEPTH_ARB            0x200C
866 #define WGL_SHARE_STENCIL_ARB          0x200D
867 #define WGL_SHARE_ACCUM_ARB            0x200E
868 #define WGL_SUPPORT_GDI_ARB            0x200F
869 #define WGL_SUPPORT_OPENGL_ARB         0x2010
870 #define WGL_DOUBLE_BUFFER_ARB          0x2011
871 #define WGL_STEREO_ARB                 0x2012
872 #define WGL_PIXEL_TYPE_ARB             0x2013
873 #define WGL_COLOR_BITS_ARB             0x2014
874 #define WGL_RED_BITS_ARB               0x2015
875 #define WGL_RED_SHIFT_ARB              0x2016
876 #define WGL_GREEN_BITS_ARB             0x2017
877 #define WGL_GREEN_SHIFT_ARB            0x2018
878 #define WGL_BLUE_BITS_ARB              0x2019
879 #define WGL_BLUE_SHIFT_ARB             0x201A
880 #define WGL_ALPHA_BITS_ARB             0x201B
881 #define WGL_ALPHA_SHIFT_ARB            0x201C
882 #define WGL_ACCUM_BITS_ARB             0x201D
883 #define WGL_ACCUM_RED_BITS_ARB         0x201E
884 #define WGL_ACCUM_GREEN_BITS_ARB       0x201F
885 #define WGL_ACCUM_BLUE_BITS_ARB        0x2020
886 #define WGL_ACCUM_ALPHA_BITS_ARB       0x2021
887 #define WGL_DEPTH_BITS_ARB             0x2022
888 #define WGL_STENCIL_BITS_ARB           0x2023
889 #define WGL_AUX_BUFFERS_ARB            0x2024
890 #define WGL_NO_ACCELERATION_ARB        0x2025
891 #define WGL_GENERIC_ACCELERATION_ARB   0x2026
892 #define WGL_FULL_ACCELERATION_ARB      0x2027
893 #define WGL_SWAP_EXCHANGE_ARB          0x2028
894 #define WGL_SWAP_COPY_ARB              0x2029
895 #define WGL_SWAP_UNDEFINED_ARB         0x202A
896 #define WGL_TYPE_RGBA_ARB              0x202B
897 #define WGL_TYPE_COLORINDEX_ARB        0x202C
898 #endif
899
900 #ifndef WGL_ARB_multisample
901 #define WGL_SAMPLE_BUFFERS_ARB         0x2041
902 #define WGL_SAMPLES_ARB                0x2042
903 #endif
904
905
906 static void IN_Init(void);
907 void VID_Init(void)
908 {
909         WNDCLASS wc;
910
911 #ifdef SUPPORTD3D
912         Cvar_RegisterVariable(&vid_dx9);
913         Cvar_RegisterVariable(&vid_dx9_hal);
914         Cvar_RegisterVariable(&vid_dx9_softvertex);
915         Cvar_RegisterVariable(&vid_dx9_triplebuffer);
916 //      Cvar_RegisterVariable(&vid_dx10);
917 //      Cvar_RegisterVariable(&vid_dx11);
918 #endif
919
920         InitCommonControls();
921         hIcon = LoadIcon (global_hInstance, MAKEINTRESOURCE (IDI_ICON1));
922
923         // Register the frame class
924         wc.style         = 0;
925         wc.lpfnWndProc   = (WNDPROC)MainWndProc;
926         wc.cbClsExtra    = 0;
927         wc.cbWndExtra    = 0;
928         wc.hInstance     = global_hInstance;
929         wc.hIcon         = hIcon;
930         wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
931         wc.hbrBackground = NULL;
932         wc.lpszMenuName  = 0;
933         wc.lpszClassName = "DarkPlacesWindowClass";
934
935         if (!RegisterClass (&wc))
936                 Con_Printf ("Couldn't register window class\n");
937
938         memset(&initialdevmode, 0, sizeof(initialdevmode));
939         EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &initialdevmode);
940
941         IN_Init();
942 }
943
944 qboolean VID_InitModeGL(viddef_mode_t *mode)
945 {
946         int i;
947         HDC hdc;
948         RECT rect;
949         MSG msg;
950         PIXELFORMATDESCRIPTOR pfd =
951         {
952                 sizeof(PIXELFORMATDESCRIPTOR),  // size of this pfd
953                 1,                              // version number
954                 PFD_DRAW_TO_WINDOW              // support window
955                 |  PFD_SUPPORT_OPENGL   // support OpenGL
956                 |  PFD_DOUBLEBUFFER ,   // double buffered
957                 PFD_TYPE_RGBA,                  // RGBA type
958                 24,                             // 24-bit color depth
959                 0, 0, 0, 0, 0, 0,               // color bits ignored
960                 0,                              // no alpha buffer
961                 0,                              // shift bit ignored
962                 0,                              // no accumulation buffer
963                 0, 0, 0, 0,                     // accum bits ignored
964                 32,                             // 32-bit z-buffer
965                 0,                              // no stencil buffer
966                 0,                              // no auxiliary buffer
967                 PFD_MAIN_PLANE,                 // main layer
968                 0,                              // reserved
969                 0, 0, 0                         // layer masks ignored
970         };
971         int windowpass;
972         int pixelformat, newpixelformat;
973         UINT numpixelformats;
974         DWORD WindowStyle, ExWindowStyle;
975         int CenterX, CenterY;
976         const char *gldrivername;
977         int depth;
978         DEVMODE thismode;
979         qboolean foundmode, foundgoodmode;
980         int *a;
981         float *af;
982         int attribs[128];
983         float attribsf[16];
984         int bpp = mode->bitsperpixel;
985         int width = mode->width;
986         int height = mode->height;
987         int refreshrate = (int)floor(mode->refreshrate+0.5);
988         int stereobuffer = mode->stereobuffer;
989         int samples = mode->samples;
990         int fullscreen = mode->fullscreen;
991
992         if (vid_initialized)
993                 Sys_Error("VID_InitMode called when video is already initialised");
994
995         // if stencil is enabled, ask for alpha too
996         if (bpp >= 32)
997         {
998                 pfd.cRedBits = 8;
999                 pfd.cGreenBits = 8;
1000                 pfd.cBlueBits = 8;
1001                 pfd.cAlphaBits = 8;
1002                 pfd.cDepthBits = 24;
1003                 pfd.cStencilBits = 8;
1004         }
1005         else
1006         {
1007                 pfd.cRedBits = 5;
1008                 pfd.cGreenBits = 5;
1009                 pfd.cBlueBits = 5;
1010                 pfd.cAlphaBits = 0;
1011                 pfd.cDepthBits = 16;
1012                 pfd.cStencilBits = 0;
1013         }
1014
1015         if (stereobuffer)
1016                 pfd.dwFlags |= PFD_STEREO;
1017
1018         a = attribs;
1019         af = attribsf;
1020         *a++ = WGL_DRAW_TO_WINDOW_ARB;
1021         *a++ = GL_TRUE;
1022         *a++ = WGL_ACCELERATION_ARB;
1023         *a++ = WGL_FULL_ACCELERATION_ARB;
1024         *a++ = WGL_DOUBLE_BUFFER_ARB;
1025         *a++ = true;
1026
1027         if (bpp >= 32)
1028         {
1029                 *a++ = WGL_RED_BITS_ARB;
1030                 *a++ = 8;
1031                 *a++ = WGL_GREEN_BITS_ARB;
1032                 *a++ = 8;
1033                 *a++ = WGL_BLUE_BITS_ARB;
1034                 *a++ = 8;
1035                 *a++ = WGL_ALPHA_BITS_ARB;
1036                 *a++ = 8;
1037                 *a++ = WGL_DEPTH_BITS_ARB;
1038                 *a++ = 24;
1039                 *a++ = WGL_STENCIL_BITS_ARB;
1040                 *a++ = 8;
1041         }
1042         else
1043         {
1044                 *a++ = WGL_RED_BITS_ARB;
1045                 *a++ = 1;
1046                 *a++ = WGL_GREEN_BITS_ARB;
1047                 *a++ = 1;
1048                 *a++ = WGL_BLUE_BITS_ARB;
1049                 *a++ = 1;
1050                 *a++ = WGL_DEPTH_BITS_ARB;
1051                 *a++ = 16;
1052         }
1053
1054         if (stereobuffer)
1055         {
1056                 *a++ = WGL_STEREO_ARB;
1057                 *a++ = GL_TRUE;
1058         }
1059
1060         if (samples > 1)
1061         {
1062                 *a++ = WGL_SAMPLE_BUFFERS_ARB;
1063                 *a++ = 1;
1064                 *a++ = WGL_SAMPLES_ARB;
1065                 *a++ = samples;
1066         }
1067
1068         *a = 0;
1069         *af = 0;
1070
1071         gldrivername = "opengl32.dll";
1072 // 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
1073         i = COM_CheckParm("-gl_driver");
1074         if (i && i < com_argc - 1)
1075                 gldrivername = com_argv[i + 1];
1076         if (!GL_OpenLibrary(gldrivername))
1077         {
1078                 Con_Printf("Unable to load GL driver %s\n", gldrivername);
1079                 return false;
1080         }
1081
1082         memset(&gdevmode, 0, sizeof(gdevmode));
1083
1084         vid_isfullscreen = false;
1085         if (fullscreen)
1086         {
1087                 if(vid_forcerefreshrate.integer)
1088                 {
1089                         foundmode = true;
1090                         gdevmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
1091                         gdevmode.dmBitsPerPel = bpp;
1092                         gdevmode.dmPelsWidth = width;
1093                         gdevmode.dmPelsHeight = height;
1094                         gdevmode.dmSize = sizeof (gdevmode);
1095                         if(refreshrate)
1096                         {
1097                                 gdevmode.dmFields |= DM_DISPLAYFREQUENCY;
1098                                 gdevmode.dmDisplayFrequency = refreshrate;
1099                         }
1100                 }
1101                 else
1102                 {
1103                         if(refreshrate == 0)
1104                                 refreshrate = initialdevmode.dmDisplayFrequency; // default vid_refreshrate to the rate of the desktop
1105
1106                         foundmode = false;
1107                         foundgoodmode = false;
1108
1109                         thismode.dmSize = sizeof(thismode);
1110                         thismode.dmDriverExtra = 0;
1111                         for(i = 0; EnumDisplaySettings(NULL, i, &thismode); ++i)
1112                         {
1113                                 if(~thismode.dmFields & (DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY))
1114                                 {
1115                                         Con_DPrintf("enumerating modes yielded a bogus item... please debug this\n");
1116                                         continue;
1117                                 }
1118                                 if(developer_extra.integer)
1119                                         Con_DPrintf("Found mode %dx%dx%dbpp %dHz... ", (int)thismode.dmPelsWidth, (int)thismode.dmPelsHeight, (int)thismode.dmBitsPerPel, (int)thismode.dmDisplayFrequency);
1120                                 if(thismode.dmBitsPerPel != (DWORD)bpp)
1121                                 {
1122                                         if(developer_extra.integer)
1123                                                 Con_DPrintf("wrong bpp\n");
1124                                         continue;
1125                                 }
1126                                 if(thismode.dmPelsWidth != (DWORD)width)
1127                                 {
1128                                         if(developer_extra.integer)
1129                                                 Con_DPrintf("wrong width\n");
1130                                         continue;
1131                                 }
1132                                 if(thismode.dmPelsHeight != (DWORD)height)
1133                                 {
1134                                         if(developer_extra.integer)
1135                                                 Con_DPrintf("wrong height\n");
1136                                         continue;
1137                                 }
1138
1139                                 if(foundgoodmode)
1140                                 {
1141                                         // if we have a good mode, make sure this mode is better than the previous one, and allowed by the refreshrate
1142                                         if(thismode.dmDisplayFrequency > (DWORD)refreshrate)
1143                                         {
1144                                                 if(developer_extra.integer)
1145                                                         Con_DPrintf("too high refresh rate\n");
1146                                                 continue;
1147                                         }
1148                                         else if(thismode.dmDisplayFrequency <= gdevmode.dmDisplayFrequency)
1149                                         {
1150                                                 if(developer_extra.integer)
1151                                                         Con_DPrintf("doesn't beat previous best match (too low)\n");
1152                                                 continue;
1153                                         }
1154                                 }
1155                                 else if(foundmode)
1156                                 {
1157                                         // we do have one, but it isn't good... make sure it has a lower frequency than the previous one
1158                                         if(thismode.dmDisplayFrequency >= gdevmode.dmDisplayFrequency)
1159                                         {
1160                                                 if(developer_extra.integer)
1161                                                         Con_DPrintf("doesn't beat previous best match (too high)\n");
1162                                                 continue;
1163                                         }
1164                                 }
1165                                 // otherwise, take anything
1166
1167                                 memcpy(&gdevmode, &thismode, sizeof(gdevmode));
1168                                 if(thismode.dmDisplayFrequency <= (DWORD)refreshrate)
1169                                         foundgoodmode = true;
1170                                 else
1171                                 {
1172                                         if(developer_extra.integer)
1173                                                 Con_DPrintf("(out of range)\n");
1174                                 }
1175                                 foundmode = true;
1176                                 if(developer_extra.integer)
1177                                         Con_DPrintf("accepted\n");
1178                         }
1179                 }
1180
1181                 if (!foundmode)
1182                 {
1183                         VID_Shutdown();
1184                         Con_Printf("Unable to find the requested mode %dx%dx%dbpp\n", width, height, bpp);
1185                         return false;
1186                 }
1187                 else if(ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
1188                 {
1189                         VID_Shutdown();
1190                         Con_Printf("Unable to change to requested mode %dx%dx%dbpp\n", width, height, bpp);
1191                         return false;
1192                 }
1193
1194                 vid_isfullscreen = true;
1195                 WindowStyle = WS_POPUP;
1196                 ExWindowStyle = WS_EX_TOPMOST;
1197         }
1198         else
1199         {
1200                 hdc = GetDC (NULL);
1201                 i = GetDeviceCaps(hdc, RASTERCAPS);
1202                 depth = GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL);
1203                 ReleaseDC (NULL, hdc);
1204                 if (i & RC_PALETTE)
1205                 {
1206                         VID_Shutdown();
1207                         Con_Print("Can't run in non-RGB mode\n");
1208                         return false;
1209                 }
1210                 if (bpp > depth)
1211                 {
1212                         VID_Shutdown();
1213                         Con_Print("A higher desktop depth is required to run this video mode\n");
1214                         return false;
1215                 }
1216
1217                 WindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
1218                 ExWindowStyle = 0;
1219         }
1220
1221         rect.top = 0;
1222         rect.left = 0;
1223         rect.right = width;
1224         rect.bottom = height;
1225         AdjustWindowRectEx(&rect, WindowStyle, false, 0);
1226
1227         if (fullscreen)
1228         {
1229                 CenterX = 0;
1230                 CenterY = 0;
1231         }
1232         else
1233         {
1234                 CenterX = (GetSystemMetrics(SM_CXSCREEN) - (rect.right - rect.left)) / 2;
1235                 CenterY = (GetSystemMetrics(SM_CYSCREEN) - (rect.bottom - rect.top)) / 2;
1236         }
1237         CenterX = max(0, CenterX);
1238         CenterY = max(0, CenterY);
1239
1240         // x and y may be changed by WM_MOVE messages
1241         window_x = CenterX;
1242         window_y = CenterY;
1243         rect.left += CenterX;
1244         rect.right += CenterX;
1245         rect.top += CenterY;
1246         rect.bottom += CenterY;
1247
1248         pixelformat = 0;
1249         newpixelformat = 0;
1250         // start out at the final windowpass if samples is 1 as it's the only feature we need extended pixel formats for
1251         for (windowpass = samples == 1;windowpass < 2;windowpass++)
1252         {
1253                 gl_extensions = "";
1254                 gl_platformextensions = "";
1255
1256                 mainwindow = CreateWindowEx (ExWindowStyle, "DarkPlacesWindowClass", gamename, WindowStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, global_hInstance, NULL);
1257                 if (!mainwindow)
1258                 {
1259                         Con_Printf("CreateWindowEx(%d, %s, %s, %d, %d, %d, %d, %d, %p, %p, %p, %p) failed\n", (int)ExWindowStyle, "DarkPlacesWindowClass", gamename, (int)WindowStyle, (int)(rect.left), (int)(rect.top), (int)(rect.right - rect.left), (int)(rect.bottom - rect.top), (void *)NULL, (void *)NULL, (void *)global_hInstance, (void *)NULL);
1260                         VID_Shutdown();
1261                         return false;
1262                 }
1263
1264                 baseDC = GetDC(mainwindow);
1265
1266                 if (!newpixelformat)
1267                         newpixelformat = ChoosePixelFormat(baseDC, &pfd);
1268                 pixelformat = newpixelformat;
1269                 if (!pixelformat)
1270                 {
1271                         VID_Shutdown();
1272                         Con_Printf("ChoosePixelFormat(%p, %p) failed\n", (void *)baseDC, (void *)&pfd);
1273                         return false;
1274                 }
1275
1276                 if (SetPixelFormat(baseDC, pixelformat, &pfd) == false)
1277                 {
1278                         VID_Shutdown();
1279                         Con_Printf("SetPixelFormat(%p, %d, %p) failed\n", (void *)baseDC, pixelformat, (void *)&pfd);
1280                         return false;
1281                 }
1282
1283                 if (!GL_CheckExtension("wgl", wglfuncs, NULL, false))
1284                 {
1285                         VID_Shutdown();
1286                         Con_Print("wgl functions not found\n");
1287                         return false;
1288                 }
1289
1290                 baseRC = qwglCreateContext(baseDC);
1291                 if (!baseRC)
1292                 {
1293                         VID_Shutdown();
1294                         Con_Print("Could not initialize GL (wglCreateContext failed).\n\nMake sure you are in 65536 color mode, and try running -window.\n");
1295                         return false;
1296                 }
1297                 if (!qwglMakeCurrent(baseDC, baseRC))
1298                 {
1299                         VID_Shutdown();
1300                         Con_Printf("wglMakeCurrent(%p, %p) failed\n", (void *)baseDC, (void *)baseRC);
1301                         return false;
1302                 }
1303
1304                 if ((qglGetString = (const GLubyte* (GLAPIENTRY *)(GLenum name))GL_GetProcAddress("glGetString")) == NULL)
1305                 {
1306                         VID_Shutdown();
1307                         Con_Print("glGetString not found\n");
1308                         return false;
1309                 }
1310                 if ((qwglGetExtensionsStringARB = (const char *(WINAPI *)(HDC hdc))GL_GetProcAddress("wglGetExtensionsStringARB")) == NULL)
1311                         Con_Print("wglGetExtensionsStringARB not found\n");
1312
1313                 gl_extensions = (const char *)qglGetString(GL_EXTENSIONS);
1314                 gl_platform = "WGL";
1315                 gl_platformextensions = "";
1316
1317                 if (qwglGetExtensionsStringARB)
1318                         gl_platformextensions = (const char *)qwglGetExtensionsStringARB(baseDC);
1319
1320                 if (!gl_extensions)
1321                         gl_extensions = "";
1322                 if (!gl_platformextensions)
1323                         gl_platformextensions = "";
1324
1325                 // now some nice Windows pain:
1326                 // we have created a window, we needed one to find out if there are
1327                 // any multisample pixel formats available, the problem is that to
1328                 // actually use one of those multisample formats we now have to
1329                 // recreate the window (yes Microsoft OpenGL really is that bad)
1330
1331                 if (windowpass == 0)
1332                 {
1333                         if (!GL_CheckExtension("WGL_ARB_pixel_format", wglpixelformatfuncs, "-noarbpixelformat", false) || !qwglChoosePixelFormatARB(baseDC, attribs, attribsf, 1, &newpixelformat, &numpixelformats) || !newpixelformat)
1334                                 break;
1335                         // ok we got one - do it all over again with newpixelformat
1336                         qwglMakeCurrent(NULL, NULL);
1337                         qwglDeleteContext(baseRC);baseRC = 0;
1338                         ReleaseDC(mainwindow, baseDC);baseDC = 0;
1339                         // eat up any messages waiting for us
1340                         while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
1341                         {
1342                                 TranslateMessage (&msg);
1343                                 DispatchMessage (&msg);
1344                         }
1345                 }
1346         }
1347
1348         /*
1349         if (!fullscreen)
1350                 SetWindowPos (mainwindow, NULL, CenterX, CenterY, 0, 0,SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME);
1351         */
1352
1353         ShowWindow (mainwindow, SW_SHOWDEFAULT);
1354         UpdateWindow (mainwindow);
1355
1356         // now we try to make sure we get the focus on the mode switch, because
1357         // sometimes in some systems we don't.  We grab the foreground, then
1358         // finish setting up, pump all our messages, and sleep for a little while
1359         // to let messages finish bouncing around the system, then we put
1360         // ourselves at the top of the z order, then grab the foreground again,
1361         // Who knows if it helps, but it probably doesn't hurt
1362         SetForegroundWindow (mainwindow);
1363
1364         while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
1365         {
1366                 TranslateMessage (&msg);
1367                 DispatchMessage (&msg);
1368         }
1369
1370         Sleep (100);
1371
1372         SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOCOPYBITS);
1373
1374         SetForegroundWindow (mainwindow);
1375
1376         // fix the leftover Alt from any Alt-Tab or the like that switched us away
1377         ClearAllStates ();
1378
1379 // COMMANDLINEOPTION: Windows WGL: -novideosync disables WGL_EXT_swap_control
1380         GL_CheckExtension("WGL_EXT_swap_control", wglswapintervalfuncs, "-novideosync", false);
1381
1382         GL_Init ();
1383
1384         //vid_menudrawfn = VID_MenuDraw;
1385         //vid_menukeyfn = VID_MenuKey;
1386         vid_usingmouse = false;
1387         vid_usinghidecursor = false;
1388         vid_usingvsync = false;
1389         vid_reallyhidden = vid_hidden = false;
1390         vid_initialized = true;
1391
1392         IN_StartupMouse ();
1393         IN_StartupJoystick ();
1394
1395         if (qwglSwapIntervalEXT)
1396         {
1397                 vid_usevsync = vid_vsync.integer != 0;
1398                 vid_usingvsync = vid_vsync.integer != 0;
1399                 qwglSwapIntervalEXT (vid_usevsync);
1400         }
1401
1402         return true;
1403 }
1404
1405 #ifdef SUPPORTD3D
1406 static D3DADAPTER_IDENTIFIER9 d3d9adapteridentifier;
1407
1408 extern cvar_t gl_info_extensions;
1409 extern cvar_t gl_info_vendor;
1410 extern cvar_t gl_info_renderer;
1411 extern cvar_t gl_info_version;
1412 extern cvar_t gl_info_platform;
1413 extern cvar_t gl_info_driver;
1414 qboolean VID_InitModeDX(viddef_mode_t *mode, int version)
1415 {
1416         int deviceindex;
1417         RECT rect;
1418         MSG msg;
1419         DWORD WindowStyle, ExWindowStyle;
1420         int CenterX, CenterY;
1421         int bpp = mode->bitsperpixel;
1422         int width = mode->width;
1423         int height = mode->height;
1424         int refreshrate = (int)floor(mode->refreshrate+0.5);
1425 //      int stereobuffer = mode->stereobuffer;
1426         int samples = mode->samples;
1427         int fullscreen = mode->fullscreen;
1428         int numdevices;
1429
1430         if (vid_initialized)
1431                 Sys_Error("VID_InitMode called when video is already initialised");
1432
1433         vid_isfullscreen = fullscreen != 0;
1434         if (fullscreen)
1435         {
1436                 WindowStyle = WS_POPUP;
1437                 ExWindowStyle = WS_EX_TOPMOST;
1438         }
1439         else
1440         {
1441                 WindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
1442                 ExWindowStyle = 0;
1443         }
1444
1445         rect.top = 0;
1446         rect.left = 0;
1447         rect.right = width;
1448         rect.bottom = height;
1449         AdjustWindowRectEx(&rect, WindowStyle, false, 0);
1450
1451         if (fullscreen)
1452         {
1453                 CenterX = 0;
1454                 CenterY = 0;
1455         }
1456         else
1457         {
1458                 CenterX = (GetSystemMetrics(SM_CXSCREEN) - (rect.right - rect.left)) / 2;
1459                 CenterY = (GetSystemMetrics(SM_CYSCREEN) - (rect.bottom - rect.top)) / 2;
1460         }
1461         CenterX = max(0, CenterX);
1462         CenterY = max(0, CenterY);
1463
1464         // x and y may be changed by WM_MOVE messages
1465         window_x = CenterX;
1466         window_y = CenterY;
1467         rect.left += CenterX;
1468         rect.right += CenterX;
1469         rect.top += CenterY;
1470         rect.bottom += CenterY;
1471
1472         gl_extensions = "";
1473         gl_platformextensions = "";
1474
1475         mainwindow = CreateWindowEx (ExWindowStyle, "DarkPlacesWindowClass", gamename, WindowStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, global_hInstance, NULL);
1476         if (!mainwindow)
1477         {
1478                 Con_Printf("CreateWindowEx(%d, %s, %s, %d, %d, %d, %d, %d, %p, %p, %p, %p) failed\n", (int)ExWindowStyle, "DarkPlacesWindowClass", gamename, (int)WindowStyle, (int)(rect.left), (int)(rect.top), (int)(rect.right - rect.left), (int)(rect.bottom - rect.top), (void *)NULL, (void *)NULL, global_hInstance, (void *)NULL);
1479                 VID_Shutdown();
1480                 return false;
1481         }
1482
1483         baseDC = GetDC(mainwindow);
1484
1485         vid_d3d9 = Direct3DCreate9(D3D_SDK_VERSION);
1486         if (!vid_d3d9)
1487                 Sys_Error("VID_InitMode: Direct3DCreate9 failed");
1488
1489         numdevices = IDirect3D9_GetAdapterCount(vid_d3d9);
1490         vid_d3d9dev = NULL;
1491         memset(&d3d9adapteridentifier, 0, sizeof(d3d9adapteridentifier));
1492         for (deviceindex = 0;deviceindex < numdevices && !vid_d3d9dev;deviceindex++)
1493         {
1494                 memset(&vid_d3dpresentparameters, 0, sizeof(vid_d3dpresentparameters));
1495 //              vid_d3dpresentparameters.Flags = D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL;
1496                 vid_d3dpresentparameters.Flags = 0;
1497                 vid_d3dpresentparameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
1498                 vid_d3dpresentparameters.hDeviceWindow = mainwindow;
1499                 vid_d3dpresentparameters.BackBufferWidth = width;
1500                 vid_d3dpresentparameters.BackBufferHeight = height;
1501                 vid_d3dpresentparameters.MultiSampleType = samples > 1 ? (D3DMULTISAMPLE_TYPE)samples : D3DMULTISAMPLE_NONE;
1502                 vid_d3dpresentparameters.BackBufferCount = fullscreen ? (vid_dx9_triplebuffer.integer ? 3 : 2) : 1;
1503                 vid_d3dpresentparameters.FullScreen_RefreshRateInHz = fullscreen ? refreshrate : 0;
1504                 vid_d3dpresentparameters.Windowed = !fullscreen;
1505                 vid_d3dpresentparameters.EnableAutoDepthStencil = true;
1506                 vid_d3dpresentparameters.AutoDepthStencilFormat = bpp > 16 ? D3DFMT_D24S8 : D3DFMT_D16;
1507                 vid_d3dpresentparameters.BackBufferFormat = fullscreen?D3DFMT_X8R8G8B8:D3DFMT_UNKNOWN;
1508                 vid_d3dpresentparameters.PresentationInterval = vid_vsync.integer ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE;
1509
1510                 memset(&d3d9adapteridentifier, 0, sizeof(d3d9adapteridentifier));
1511                 IDirect3D9_GetAdapterIdentifier(vid_d3d9, deviceindex, 0, &d3d9adapteridentifier);
1512
1513                 IDirect3D9_CreateDevice(vid_d3d9, deviceindex, vid_dx9_hal.integer ? D3DDEVTYPE_HAL : D3DDEVTYPE_REF, mainwindow, vid_dx9_softvertex.integer ? D3DCREATE_SOFTWARE_VERTEXPROCESSING : D3DCREATE_HARDWARE_VERTEXPROCESSING, &vid_d3dpresentparameters, &vid_d3d9dev);
1514         }
1515
1516         if (!vid_d3d9dev)
1517         {
1518                 VID_Shutdown();
1519                 return false;
1520         }
1521
1522         IDirect3DDevice9_GetDeviceCaps(vid_d3d9dev, &vid_d3d9caps);
1523
1524         Con_Printf("Using D3D9 device: %s\n", d3d9adapteridentifier.Description);
1525         gl_extensions = "";
1526         gl_platform = "D3D9";
1527         gl_platformextensions = "";
1528
1529         ShowWindow (mainwindow, SW_SHOWDEFAULT);
1530         UpdateWindow (mainwindow);
1531
1532         // now we try to make sure we get the focus on the mode switch, because
1533         // sometimes in some systems we don't.  We grab the foreground, then
1534         // finish setting up, pump all our messages, and sleep for a little while
1535         // to let messages finish bouncing around the system, then we put
1536         // ourselves at the top of the z order, then grab the foreground again,
1537         // Who knows if it helps, but it probably doesn't hurt
1538         SetForegroundWindow (mainwindow);
1539
1540         while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
1541         {
1542                 TranslateMessage (&msg);
1543                 DispatchMessage (&msg);
1544         }
1545
1546         Sleep (100);
1547
1548         SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOCOPYBITS);
1549
1550         SetForegroundWindow (mainwindow);
1551
1552         // fix the leftover Alt from any Alt-Tab or the like that switched us away
1553         ClearAllStates ();
1554
1555         gl_renderer = d3d9adapteridentifier.Description;
1556         gl_vendor = d3d9adapteridentifier.Driver;
1557         gl_version = "";
1558         gl_extensions = "";
1559
1560         Con_Printf("D3D9 adapter info:\n");
1561         Con_Printf("Description: %s\n", d3d9adapteridentifier.Description);
1562         Con_Printf("DeviceId: %x\n", (unsigned int)d3d9adapteridentifier.DeviceId);
1563         Con_Printf("DeviceName: %p\n", d3d9adapteridentifier.DeviceName);
1564         Con_Printf("Driver: %s\n", d3d9adapteridentifier.Driver);
1565         Con_Printf("DriverVersion: %08x%08x\n", (unsigned int)d3d9adapteridentifier.DriverVersion.u.HighPart, (unsigned int)d3d9adapteridentifier.DriverVersion.u.LowPart);
1566         Con_DPrintf("GL_EXTENSIONS: %s\n", gl_extensions);
1567         Con_DPrintf("%s_EXTENSIONS: %s\n", gl_platform, gl_platformextensions);
1568
1569         // clear the extension flags
1570         memset(&vid.support, 0, sizeof(vid.support));
1571         Cvar_SetQuick(&gl_info_extensions, "");
1572
1573         // D3D9 requires BGRA
1574         vid.forcetextype = TEXTYPE_BGRA;
1575
1576         vid.forcevbo = false;
1577         vid.support.arb_depth_texture = true;
1578         vid.support.arb_draw_buffers = vid_d3d9caps.NumSimultaneousRTs > 1;
1579         vid.support.arb_occlusion_query = true; // can't find a cap for this
1580         vid.support.arb_shadow = true;
1581         vid.support.arb_texture_compression = true;
1582         vid.support.arb_texture_cube_map = true;
1583         vid.support.arb_texture_non_power_of_two = (vid_d3d9caps.TextureCaps & D3DPTEXTURECAPS_POW2) == 0;
1584         vid.support.arb_vertex_buffer_object = true;
1585         vid.support.ext_blend_subtract = true;
1586         vid.support.ext_draw_range_elements = true;
1587         vid.support.ext_framebuffer_object = true;
1588         vid.support.ext_texture_3d = true;
1589         vid.support.ext_texture_compression_s3tc = true;
1590         vid.support.ext_texture_filter_anisotropic = true;
1591         vid.support.ati_separate_stencil = (vid_d3d9caps.StencilCaps & D3DSTENCILCAPS_TWOSIDED) != 0;
1592         vid.support.ext_texture_srgb = false; // FIXME use D3DSAMP_SRGBTEXTURE if CheckDeviceFormat agrees
1593
1594         vid.maxtexturesize_2d = min(vid_d3d9caps.MaxTextureWidth, vid_d3d9caps.MaxTextureHeight);
1595         vid.maxtexturesize_3d = vid_d3d9caps.MaxVolumeExtent;
1596         vid.maxtexturesize_cubemap = vid.maxtexturesize_2d;
1597         vid.texunits = 4;
1598         vid.teximageunits = vid_d3d9caps.MaxSimultaneousTextures;
1599         vid.texarrayunits = 8; // can't find a caps field for this?
1600         vid.max_anisotropy = vid_d3d9caps.MaxAnisotropy;
1601         vid.maxdrawbuffers = vid_d3d9caps.NumSimultaneousRTs;
1602
1603         vid.texunits = bound(4, vid.texunits, MAX_TEXTUREUNITS);
1604         vid.teximageunits = bound(16, vid.teximageunits, MAX_TEXTUREUNITS);
1605         vid.texarrayunits = bound(8, vid.texarrayunits, MAX_TEXTUREUNITS);
1606         Con_DPrintf("Using D3D9.0 rendering path - %i texture matrix, %i texture images, %i texcoords, shadowmapping supported%s\n", vid.texunits, vid.teximageunits, vid.texarrayunits, vid.maxdrawbuffers > 1 ? ", MRT detected (allows prepass deferred lighting)" : "");
1607         vid.renderpath = RENDERPATH_D3D9;
1608         vid.sRGBcapable2D = false;
1609         vid.sRGBcapable3D = true;
1610         vid.useinterleavedarrays = true;
1611
1612         Cvar_SetQuick(&gl_info_vendor, gl_vendor);
1613         Cvar_SetQuick(&gl_info_renderer, gl_renderer);
1614         Cvar_SetQuick(&gl_info_version, gl_version);
1615         Cvar_SetQuick(&gl_info_platform, gl_platform ? gl_platform : "");
1616         Cvar_SetQuick(&gl_info_driver, gl_driver);
1617
1618         // LordHavoc: report supported extensions
1619         Con_DPrintf("\nQuakeC extensions for server and client: %s\nQuakeC extensions for menu: %s\n", vm_sv_extensions, vm_m_extensions );
1620
1621         // clear to black (loading plaque will be seen over this)
1622         IDirect3DDevice9_Clear(vid_d3d9dev, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
1623         IDirect3DDevice9_BeginScene(vid_d3d9dev);
1624         IDirect3DDevice9_EndScene(vid_d3d9dev);
1625         IDirect3DDevice9_Present(vid_d3d9dev, NULL, NULL, NULL, NULL);
1626         // because the only time we end/begin scene is in VID_Finish, we'd better start a scene now...
1627         IDirect3DDevice9_BeginScene(vid_d3d9dev);
1628         vid_begunscene = true;
1629
1630         //vid_menudrawfn = VID_MenuDraw;
1631         //vid_menukeyfn = VID_MenuKey;
1632         vid_usingmouse = false;
1633         vid_usinghidecursor = false;
1634         vid_usingvsync = false;
1635         vid_hidden = vid_reallyhidden = false;
1636         vid_initialized = true;
1637
1638         IN_StartupMouse ();
1639         IN_StartupJoystick ();
1640
1641         return true;
1642 }
1643 #endif
1644
1645 qboolean VID_InitModeSOFT(viddef_mode_t *mode)
1646 {
1647         int i;
1648         HDC hdc;
1649         RECT rect;
1650         MSG msg;
1651         int pixelformat, newpixelformat;
1652         DWORD WindowStyle, ExWindowStyle;
1653         int CenterX, CenterY;
1654         int depth;
1655         DEVMODE thismode;
1656         qboolean foundmode, foundgoodmode;
1657         int bpp = mode->bitsperpixel;
1658         int width = mode->width;
1659         int height = mode->height;
1660         int refreshrate = (int)floor(mode->refreshrate+0.5);
1661         int fullscreen = mode->fullscreen;
1662
1663         if (vid_initialized)
1664                 Sys_Error("VID_InitMode called when video is already initialised");
1665
1666         memset(&gdevmode, 0, sizeof(gdevmode));
1667
1668         vid_isfullscreen = false;
1669         if (fullscreen)
1670         {
1671                 if(vid_forcerefreshrate.integer)
1672                 {
1673                         foundmode = true;
1674                         gdevmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
1675                         gdevmode.dmBitsPerPel = bpp;
1676                         gdevmode.dmPelsWidth = width;
1677                         gdevmode.dmPelsHeight = height;
1678                         gdevmode.dmSize = sizeof (gdevmode);
1679                         if(refreshrate)
1680                         {
1681                                 gdevmode.dmFields |= DM_DISPLAYFREQUENCY;
1682                                 gdevmode.dmDisplayFrequency = refreshrate;
1683                         }
1684                 }
1685                 else
1686                 {
1687                         if(refreshrate == 0)
1688                                 refreshrate = initialdevmode.dmDisplayFrequency; // default vid_refreshrate to the rate of the desktop
1689
1690                         foundmode = false;
1691                         foundgoodmode = false;
1692
1693                         thismode.dmSize = sizeof(thismode);
1694                         thismode.dmDriverExtra = 0;
1695                         for(i = 0; EnumDisplaySettings(NULL, i, &thismode); ++i)
1696                         {
1697                                 if(~thismode.dmFields & (DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY))
1698                                 {
1699                                         Con_DPrintf("enumerating modes yielded a bogus item... please debug this\n");
1700                                         continue;
1701                                 }
1702                                 if(developer_extra.integer)
1703                                         Con_DPrintf("Found mode %dx%dx%dbpp %dHz... ", (int)thismode.dmPelsWidth, (int)thismode.dmPelsHeight, (int)thismode.dmBitsPerPel, (int)thismode.dmDisplayFrequency);
1704                                 if(thismode.dmBitsPerPel != (DWORD)bpp)
1705                                 {
1706                                         if(developer_extra.integer)
1707                                                 Con_DPrintf("wrong bpp\n");
1708                                         continue;
1709                                 }
1710                                 if(thismode.dmPelsWidth != (DWORD)width)
1711                                 {
1712                                         if(developer_extra.integer)
1713                                                 Con_DPrintf("wrong width\n");
1714                                         continue;
1715                                 }
1716                                 if(thismode.dmPelsHeight != (DWORD)height)
1717                                 {
1718                                         if(developer_extra.integer)
1719                                                 Con_DPrintf("wrong height\n");
1720                                         continue;
1721                                 }
1722
1723                                 if(foundgoodmode)
1724                                 {
1725                                         // if we have a good mode, make sure this mode is better than the previous one, and allowed by the refreshrate
1726                                         if(thismode.dmDisplayFrequency > (DWORD)refreshrate)
1727                                         {
1728                                                 if(developer_extra.integer)
1729                                                         Con_DPrintf("too high refresh rate\n");
1730                                                 continue;
1731                                         }
1732                                         else if(thismode.dmDisplayFrequency <= gdevmode.dmDisplayFrequency)
1733                                         {
1734                                                 if(developer_extra.integer)
1735                                                         Con_DPrintf("doesn't beat previous best match (too low)\n");
1736                                                 continue;
1737                                         }
1738                                 }
1739                                 else if(foundmode)
1740                                 {
1741                                         // we do have one, but it isn't good... make sure it has a lower frequency than the previous one
1742                                         if(thismode.dmDisplayFrequency >= gdevmode.dmDisplayFrequency)
1743                                         {
1744                                                 if(developer_extra.integer)
1745                                                         Con_DPrintf("doesn't beat previous best match (too high)\n");
1746                                                 continue;
1747                                         }
1748                                 }
1749                                 // otherwise, take anything
1750
1751                                 memcpy(&gdevmode, &thismode, sizeof(gdevmode));
1752                                 if(thismode.dmDisplayFrequency <= (DWORD)refreshrate)
1753                                         foundgoodmode = true;
1754                                 else
1755                                 {
1756                                         if(developer_extra.integer)
1757                                                 Con_DPrintf("(out of range)\n");
1758                                 }
1759                                 foundmode = true;
1760                                 if(developer_extra.integer)
1761                                         Con_DPrintf("accepted\n");
1762                         }
1763                 }
1764
1765                 if (!foundmode)
1766                 {
1767                         VID_Shutdown();
1768                         Con_Printf("Unable to find the requested mode %dx%dx%dbpp\n", width, height, bpp);
1769                         return false;
1770                 }
1771                 else if(ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
1772                 {
1773                         VID_Shutdown();
1774                         Con_Printf("Unable to change to requested mode %dx%dx%dbpp\n", width, height, bpp);
1775                         return false;
1776                 }
1777
1778                 vid_isfullscreen = true;
1779                 WindowStyle = WS_POPUP;
1780                 ExWindowStyle = WS_EX_TOPMOST;
1781         }
1782         else
1783         {
1784                 hdc = GetDC (NULL);
1785                 i = GetDeviceCaps(hdc, RASTERCAPS);
1786                 depth = GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL);
1787                 ReleaseDC (NULL, hdc);
1788                 if (i & RC_PALETTE)
1789                 {
1790                         VID_Shutdown();
1791                         Con_Print("Can't run in non-RGB mode\n");
1792                         return false;
1793                 }
1794                 if (bpp > depth)
1795                 {
1796                         VID_Shutdown();
1797                         Con_Print("A higher desktop depth is required to run this video mode\n");
1798                         return false;
1799                 }
1800
1801                 WindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
1802                 ExWindowStyle = 0;
1803         }
1804
1805         rect.top = 0;
1806         rect.left = 0;
1807         rect.right = width;
1808         rect.bottom = height;
1809         AdjustWindowRectEx(&rect, WindowStyle, false, 0);
1810
1811         if (fullscreen)
1812         {
1813                 CenterX = 0;
1814                 CenterY = 0;
1815         }
1816         else
1817         {
1818                 CenterX = (GetSystemMetrics(SM_CXSCREEN) - (rect.right - rect.left)) / 2;
1819                 CenterY = (GetSystemMetrics(SM_CYSCREEN) - (rect.bottom - rect.top)) / 2;
1820         }
1821         CenterX = max(0, CenterX);
1822         CenterY = max(0, CenterY);
1823
1824         // x and y may be changed by WM_MOVE messages
1825         window_x = CenterX;
1826         window_y = CenterY;
1827         rect.left += CenterX;
1828         rect.right += CenterX;
1829         rect.top += CenterY;
1830         rect.bottom += CenterY;
1831
1832         pixelformat = 0;
1833         newpixelformat = 0;
1834         gl_extensions = "";
1835         gl_platformextensions = "";
1836
1837         mainwindow = CreateWindowEx (ExWindowStyle, "DarkPlacesWindowClass", gamename, WindowStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, global_hInstance, NULL);
1838         if (!mainwindow)
1839         {
1840                 Con_Printf("CreateWindowEx(%d, %s, %s, %d, %d, %d, %d, %d, %p, %p, %p, %p) failed\n", (int)ExWindowStyle, "DarkPlacesWindowClass", gamename, (int)WindowStyle, (int)(rect.left), (int)(rect.top), (int)(rect.right - rect.left), (int)(rect.bottom - rect.top), (void *)NULL, (void *)NULL, (void *)global_hInstance, (void *)NULL);
1841                 VID_Shutdown();
1842                 return false;
1843         }
1844
1845         baseDC = GetDC(mainwindow);
1846         vid.softpixels = NULL;
1847         memset(&vid_softbmi, 0, sizeof(vid_softbmi));
1848         vid_softbmi.bmiHeader.biSize = sizeof(vid_softbmi.bmiHeader);
1849         vid_softbmi.bmiHeader.biWidth = width;
1850         vid_softbmi.bmiHeader.biHeight = -height; // negative to make a top-down bitmap
1851         vid_softbmi.bmiHeader.biPlanes = 1;
1852         vid_softbmi.bmiHeader.biBitCount = 32;
1853         vid_softbmi.bmiHeader.biCompression = BI_RGB;
1854         vid_softbmi.bmiHeader.biSizeImage = width*height*4;
1855         vid_softbmi.bmiHeader.biClrUsed = 256;
1856         vid_softbmi.bmiHeader.biClrImportant = 256;
1857         vid_softdibhandle = CreateDIBSection(baseDC, &vid_softbmi, DIB_RGB_COLORS, (void **)&vid.softpixels, NULL, 0);
1858         if (!vid_softdibhandle)
1859         {
1860                 Con_Printf("CreateDIBSection failed\n");
1861                 VID_Shutdown();
1862                 return false;
1863         }
1864
1865         vid_softhdc = CreateCompatibleDC(baseDC);
1866         vid_softhdc_backup = SelectObject(vid_softhdc, vid_softdibhandle);
1867         if (!vid_softhdc_backup)
1868         {
1869                 Con_Printf("SelectObject failed\n");
1870                 VID_Shutdown();
1871                 return false;
1872         }
1873 //      ReleaseDC(mainwindow, baseDC);
1874 //      baseDC = NULL;
1875
1876         vid.softdepthpixels = (unsigned int *)calloc(1, mode->width * mode->height * 4);
1877         if (DPSOFTRAST_Init(mode->width, mode->height, vid_soft_threads.integer, vid_soft_interlace.integer, (unsigned int *)vid.softpixels, (unsigned int *)vid.softdepthpixels) < 0)
1878         {
1879                 Con_Printf("Failed to initialize software rasterizer\n");
1880                 VID_Shutdown();
1881                 return false;
1882         }
1883
1884         VID_Soft_SharedSetup();
1885
1886         ShowWindow (mainwindow, SW_SHOWDEFAULT);
1887         UpdateWindow (mainwindow);
1888
1889         // now we try to make sure we get the focus on the mode switch, because
1890         // sometimes in some systems we don't.  We grab the foreground, then
1891         // finish setting up, pump all our messages, and sleep for a little while
1892         // to let messages finish bouncing around the system, then we put
1893         // ourselves at the top of the z order, then grab the foreground again,
1894         // Who knows if it helps, but it probably doesn't hurt
1895         SetForegroundWindow (mainwindow);
1896
1897         while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
1898         {
1899                 TranslateMessage (&msg);
1900                 DispatchMessage (&msg);
1901         }
1902
1903         Sleep (100);
1904
1905         SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOCOPYBITS);
1906
1907         SetForegroundWindow (mainwindow);
1908
1909         // fix the leftover Alt from any Alt-Tab or the like that switched us away
1910         ClearAllStates ();
1911
1912         //vid_menudrawfn = VID_MenuDraw;
1913         //vid_menukeyfn = VID_MenuKey;
1914         vid_usingmouse = false;
1915         vid_usinghidecursor = false;
1916         vid_usingvsync = false;
1917         vid_reallyhidden = vid_hidden = false;
1918         vid_initialized = true;
1919
1920         IN_StartupMouse ();
1921         IN_StartupJoystick ();
1922
1923         return true;
1924 }
1925
1926 qboolean VID_InitMode(viddef_mode_t *mode)
1927 {
1928 #ifdef SSE_POSSIBLE
1929         if (vid_soft.integer)
1930                 return VID_InitModeSOFT(mode);
1931 #endif
1932 #ifdef SUPPORTD3D
1933 //      if (vid_dx11.integer)
1934 //              return VID_InitModeDX(mode, 11);
1935 //      if (vid_dx10.integer)
1936 //              return VID_InitModeDX(mode, 10);
1937         if (vid_dx9.integer)
1938                 return VID_InitModeDX(mode, 9);
1939 #endif
1940         return VID_InitModeGL(mode);
1941 }
1942
1943
1944 static void IN_Shutdown(void);
1945 void VID_Shutdown (void)
1946 {
1947         qboolean isgl;
1948         if(vid_initialized == false)
1949                 return;
1950
1951         VID_SetMouse(false, false, false);
1952         VID_RestoreSystemGamma();
1953
1954         vid_initialized = false;
1955         isgl = gldll != NULL;
1956         IN_Shutdown();
1957         gl_driver[0] = 0;
1958         gl_extensions = "";
1959         gl_platform = "";
1960         gl_platformextensions = "";
1961         if (vid_softhdc)
1962         {
1963                 SelectObject(vid_softhdc, vid_softhdc_backup);
1964                 ReleaseDC(mainwindow, vid_softhdc);
1965         }
1966         vid_softhdc = NULL;
1967         vid_softhdc_backup = NULL;
1968         if (vid_softdibhandle)
1969                 DeleteObject(vid_softdibhandle);
1970         vid_softdibhandle = NULL;
1971         vid.softpixels = NULL;
1972         if (vid.softdepthpixels)
1973                 free(vid.softdepthpixels);
1974         vid.softdepthpixels = NULL;
1975 #ifdef SUPPORTD3D
1976         if (vid_d3d9dev)
1977         {
1978                 if (vid_begunscene)
1979                         IDirect3DDevice9_EndScene(vid_d3d9dev);
1980                 vid_begunscene = false;
1981 //              Cmd_ExecuteString("r_texturestats", src_command);
1982 //              Cmd_ExecuteString("memlist", src_command);
1983                 IDirect3DDevice9_Release(vid_d3d9dev);
1984         }
1985         vid_d3d9dev = NULL;
1986         if (vid_d3d9)
1987                 IDirect3D9_Release(vid_d3d9);
1988         vid_d3d9 = NULL;
1989 #endif
1990         if (qwglMakeCurrent)
1991                 qwglMakeCurrent(NULL, NULL);
1992         qwglMakeCurrent = NULL;
1993         if (baseRC && qwglDeleteContext)
1994                 qwglDeleteContext(baseRC);
1995         qwglDeleteContext = NULL;
1996         // close the library before we get rid of the window
1997         GL_CloseLibrary();
1998         if (baseDC && mainwindow)
1999                 ReleaseDC(mainwindow, baseDC);
2000         baseDC = NULL;
2001         AppActivate(false, false);
2002         if (mainwindow)
2003                 DestroyWindow(mainwindow);
2004         mainwindow = 0;
2005         if (vid_isfullscreen && isgl)
2006                 ChangeDisplaySettings (NULL, 0);
2007         vid_isfullscreen = false;
2008 }
2009
2010 void VID_SetMouse(qboolean fullscreengrab, qboolean relative, qboolean hidecursor)
2011 {
2012         static qboolean restore_spi;
2013         static int originalmouseparms[3];
2014
2015         if (!mouseinitialized)
2016                 return;
2017
2018         if (relative)
2019         {
2020                 if (!vid_usingmouse)
2021                 {
2022                         vid_usingmouse = true;
2023                         cl_ignoremousemoves = 2;
2024 #ifdef SUPPORTDIRECTX
2025                         if (dinput && g_pMouse)
2026                         {
2027                                 IDirectInputDevice_Acquire(g_pMouse);
2028                                 dinput_acquired = true;
2029                         }
2030                         else
2031 #endif
2032                         {
2033                                 RECT window_rect;
2034                                 window_rect.left = window_x;
2035                                 window_rect.top = window_y;
2036                                 window_rect.right = window_x + vid.width;
2037                                 window_rect.bottom = window_y + vid.height;
2038
2039                                 // change mouse settings to turn off acceleration
2040 // COMMANDLINEOPTION: Windows GDI Input: -noforcemparms disables setting of mouse parameters (not used with -dinput, windows only)
2041                                 if (!COM_CheckParm ("-noforcemparms") && SystemParametersInfo (SPI_GETMOUSE, 0, originalmouseparms, 0))
2042                                 {
2043                                         int newmouseparms[3];
2044                                         newmouseparms[0] = 0; // threshold to double movement (only if accel level is >= 1)
2045                                         newmouseparms[1] = 0; // threshold to quadruple movement (only if accel level is >= 2)
2046                                         newmouseparms[2] = 0; // maximum level of acceleration (0 = off)
2047                                         restore_spi = SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0) != FALSE;
2048                                 }
2049                                 else
2050                                         restore_spi = false;
2051                                 SetCursorPos ((window_x + vid.width / 2), (window_y + vid.height / 2));
2052
2053                                 SetCapture (mainwindow);
2054                                 ClipCursor (&window_rect);
2055                         }
2056                 }
2057         }
2058         else
2059         {
2060                 if (vid_usingmouse)
2061                 {
2062                         vid_usingmouse = false;
2063                         cl_ignoremousemoves = 2;
2064 #ifdef SUPPORTDIRECTX
2065                         if (dinput_acquired)
2066                         {
2067                                 IDirectInputDevice_Unacquire(g_pMouse);
2068                                 dinput_acquired = false;
2069                         }
2070                         else
2071 #endif
2072                         {
2073                                 // restore system mouseparms if we changed them
2074                                 if (restore_spi)
2075                                         SystemParametersInfo (SPI_SETMOUSE, 0, originalmouseparms, 0);
2076                                 restore_spi = false;
2077                                 ClipCursor (NULL);
2078                                 ReleaseCapture ();
2079                         }
2080                 }
2081         }
2082
2083         if (vid_usinghidecursor != hidecursor)
2084         {
2085                 vid_usinghidecursor = hidecursor;
2086                 ShowCursor (!hidecursor);
2087         }
2088 }
2089
2090
2091 #ifdef SUPPORTDIRECTX
2092 /*
2093 ===========
2094 IN_InitDInput
2095 ===========
2096 */
2097 static qboolean IN_InitDInput (void)
2098 {
2099     HRESULT             hr;
2100         DIPROPDWORD     dipdw = {
2101                 {
2102                         sizeof(DIPROPDWORD),        // diph.dwSize
2103                         sizeof(DIPROPHEADER),       // diph.dwHeaderSize
2104                         0,                          // diph.dwObj
2105                         DIPH_DEVICE,                // diph.dwHow
2106                 },
2107                 DINPUT_BUFFERSIZE,              // dwData
2108         };
2109
2110         if (!hInstDI)
2111         {
2112                 hInstDI = LoadLibrary("dinput.dll");
2113
2114                 if (hInstDI == NULL)
2115                 {
2116                         Con_Print("Couldn't load dinput.dll\n");
2117                         return false;
2118                 }
2119         }
2120
2121         if (!pDirectInputCreate)
2122         {
2123                 pDirectInputCreate = (HRESULT (__stdcall *)(HINSTANCE,DWORD,LPDIRECTINPUT *,LPUNKNOWN))GetProcAddress(hInstDI,"DirectInputCreateA");
2124
2125                 if (!pDirectInputCreate)
2126                 {
2127                         Con_Print("Couldn't get DI proc addr\n");
2128                         return false;
2129                 }
2130         }
2131
2132 // register with DirectInput and get an IDirectInput to play with.
2133         hr = iDirectInputCreate(global_hInstance, DIRECTINPUT_VERSION, &g_pdi, NULL);
2134
2135         if (FAILED(hr))
2136         {
2137                 return false;
2138         }
2139
2140 // obtain an interface to the system mouse device.
2141 #ifdef __cplusplus
2142         hr = IDirectInput_CreateDevice(g_pdi, GUID_SysMouse, &g_pMouse, NULL);
2143 #else
2144         hr = IDirectInput_CreateDevice(g_pdi, &GUID_SysMouse, &g_pMouse, NULL);
2145 #endif
2146
2147         if (FAILED(hr))
2148         {
2149                 Con_Print("Couldn't open DI mouse device\n");
2150                 return false;
2151         }
2152
2153 // set the data format to "mouse format".
2154         hr = IDirectInputDevice_SetDataFormat(g_pMouse, &c_dfDIMouse);
2155
2156         if (FAILED(hr))
2157         {
2158                 Con_Print("Couldn't set DI mouse format\n");
2159                 return false;
2160         }
2161
2162 // set the cooperativity level.
2163         hr = IDirectInputDevice_SetCooperativeLevel(g_pMouse, mainwindow,
2164                         DISCL_EXCLUSIVE | DISCL_FOREGROUND);
2165
2166         if (FAILED(hr))
2167         {
2168                 Con_Print("Couldn't set DI coop level\n");
2169                 return false;
2170         }
2171
2172
2173 // set the buffer size to DINPUT_BUFFERSIZE elements.
2174 // the buffer size is a DWORD property associated with the device
2175         hr = IDirectInputDevice_SetProperty(g_pMouse, DIPROP_BUFFERSIZE, &dipdw.diph);
2176
2177         if (FAILED(hr))
2178         {
2179                 Con_Print("Couldn't set DI buffersize\n");
2180                 return false;
2181         }
2182
2183         return true;
2184 }
2185 #endif
2186
2187
2188 /*
2189 ===========
2190 IN_StartupMouse
2191 ===========
2192 */
2193 static void IN_StartupMouse (void)
2194 {
2195         if (COM_CheckParm ("-nomouse"))
2196                 return;
2197
2198         mouseinitialized = true;
2199
2200 #ifdef SUPPORTDIRECTX
2201 // COMMANDLINEOPTION: Windows Input: -dinput enables DirectInput for mouse/joystick input
2202         if (COM_CheckParm ("-dinput"))
2203                 dinput = IN_InitDInput ();
2204
2205         if (dinput)
2206                 Con_Print("DirectInput initialized\n");
2207         else
2208                 Con_Print("DirectInput not initialized\n");
2209 #endif
2210
2211         mouse_buttons = 10;
2212 }
2213
2214
2215 /*
2216 ===========
2217 IN_MouseMove
2218 ===========
2219 */
2220 static void IN_MouseMove (void)
2221 {
2222         POINT current_pos;
2223
2224         GetCursorPos (&current_pos);
2225         in_windowmouse_x = current_pos.x - window_x;
2226         in_windowmouse_y = current_pos.y - window_y;
2227
2228         if (!vid_usingmouse)
2229                 return;
2230
2231 #ifdef SUPPORTDIRECTX
2232         if (dinput_acquired)
2233         {
2234                 int i;
2235                 DIDEVICEOBJECTDATA      od;
2236                 DWORD                           dwElements;
2237                 HRESULT                         hr;
2238
2239                 for (;;)
2240                 {
2241                         dwElements = 1;
2242
2243                         hr = IDirectInputDevice_GetDeviceData(g_pMouse,
2244                                         sizeof(DIDEVICEOBJECTDATA), &od, &dwElements, 0);
2245
2246                         if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED))
2247                         {
2248                                 IDirectInputDevice_Acquire(g_pMouse);
2249                                 break;
2250                         }
2251
2252                         /* Unable to read data or no data available */
2253                         if (FAILED(hr) || dwElements == 0)
2254                                 break;
2255
2256                         /* Look at the element to see what happened */
2257
2258                         if ((int)od.dwOfs == DIMOFS_X)
2259                                 in_mouse_x += (LONG) od.dwData;
2260                         if ((int)od.dwOfs == DIMOFS_Y)
2261                                 in_mouse_y += (LONG) od.dwData;
2262                         if ((int)od.dwOfs == DIMOFS_Z)
2263                         {
2264                                 if((LONG)od.dwData < 0)
2265                                 {
2266                                         Key_Event(K_MWHEELDOWN, 0, true);
2267                                         Key_Event(K_MWHEELDOWN, 0, false);
2268                                 }
2269                                 else if((LONG)od.dwData > 0)
2270                                 {
2271                                         Key_Event(K_MWHEELUP, 0, true);
2272                                         Key_Event(K_MWHEELUP, 0, false);
2273                                 }
2274                         }
2275                         if ((int)od.dwOfs == DIMOFS_BUTTON0)
2276                                 mstate_di = (mstate_di & ~1) | ((od.dwData & 0x80) >> 7);
2277                         if ((int)od.dwOfs == DIMOFS_BUTTON1)
2278                                 mstate_di = (mstate_di & ~2) | ((od.dwData & 0x80) >> 6);
2279                         if ((int)od.dwOfs == DIMOFS_BUTTON2)
2280                                 mstate_di = (mstate_di & ~4) | ((od.dwData & 0x80) >> 5);
2281                         if ((int)od.dwOfs == DIMOFS_BUTTON3)
2282                                 mstate_di = (mstate_di & ~8) | ((od.dwData & 0x80) >> 4);
2283                 }
2284
2285                 // perform button actions
2286                 for (i=0 ; i<mouse_buttons && i < 16 ; i++)
2287                         if ((mstate_di ^ mouse_oldbuttonstate) & (1<<i))
2288                                 Key_Event (buttonremap[i], 0, (mstate_di & (1<<i)) != 0);
2289                 mouse_oldbuttonstate = mstate_di;
2290         }
2291         else
2292 #endif
2293         {
2294                 in_mouse_x += in_windowmouse_x - (int)(vid.width / 2);
2295                 in_mouse_y += in_windowmouse_y - (int)(vid.height / 2);
2296
2297                 // if the mouse has moved, force it to the center, so there's room to move
2298                 if (in_mouse_x || in_mouse_y)
2299                         SetCursorPos ((window_x + vid.width / 2), (window_y + vid.height / 2));
2300         }
2301 }
2302
2303
2304 /*
2305 ===========
2306 IN_Move
2307 ===========
2308 */
2309 void IN_Move (void)
2310 {
2311         if (vid_activewindow && !vid_reallyhidden)
2312         {
2313                 IN_MouseMove ();
2314                 IN_JoyMove ();
2315         }
2316 }
2317
2318
2319 /*
2320 ===============
2321 IN_StartupJoystick
2322 ===============
2323 */
2324 static void IN_StartupJoystick (void)
2325 {
2326         int                     numdevs;
2327         JOYCAPS         jc;
2328         MMRESULT        mmr;
2329         mmr = 0;
2330
2331         // assume no joystick
2332         joy_avail = false;
2333
2334         // abort startup if user requests no joystick
2335 // COMMANDLINEOPTION: Windows Input: -nojoy disables joystick support, may be a small speed increase
2336         if (COM_CheckParm ("-nojoy"))
2337                 return;
2338
2339         // verify joystick driver is present
2340         if ((numdevs = joyGetNumDevs ()) == 0)
2341         {
2342                 Con_Print("\njoystick not found -- driver not present\n\n");
2343                 return;
2344         }
2345
2346         // cycle through the joystick ids for the first valid one
2347         for (joy_id=0 ; joy_id<numdevs ; joy_id++)
2348         {
2349                 memset (&ji, 0, sizeof(ji));
2350                 ji.dwSize = sizeof(ji);
2351                 ji.dwFlags = JOY_RETURNCENTERED;
2352
2353                 if ((mmr = joyGetPosEx (joy_id, &ji)) == JOYERR_NOERROR)
2354                         break;
2355         }
2356
2357         // abort startup if we didn't find a valid joystick
2358         if (mmr != JOYERR_NOERROR)
2359         {
2360                 Con_Printf("\njoystick not found -- no valid joysticks (%x)\n\n", mmr);
2361                 return;
2362         }
2363
2364         // get the capabilities of the selected joystick
2365         // abort startup if command fails
2366         memset (&jc, 0, sizeof(jc));
2367         if ((mmr = joyGetDevCaps (joy_id, &jc, sizeof(jc))) != JOYERR_NOERROR)
2368         {
2369                 Con_Printf("\njoystick not found -- invalid joystick capabilities (%x)\n\n", mmr);
2370                 return;
2371         }
2372
2373         // save the joystick's number of buttons and POV status
2374         joy_numbuttons = jc.wNumButtons;
2375         joy_haspov = (jc.wCaps & JOYCAPS_HASPOV) != 0;
2376
2377         // old button and POV states default to no buttons pressed
2378         joy_oldbuttonstate = joy_oldpovstate = 0;
2379
2380         // mark the joystick as available and advanced initialization not completed
2381         // this is needed as cvars are not available during initialization
2382
2383         joy_avail = true;
2384         joy_advancedinit = false;
2385
2386         Con_Print("\njoystick detected\n\n");
2387 }
2388
2389
2390 /*
2391 ===========
2392 RawValuePointer
2393 ===========
2394 */
2395 static PDWORD RawValuePointer (int axis)
2396 {
2397         switch (axis)
2398         {
2399         case JOY_AXIS_X:
2400                 return &ji.dwXpos;
2401         case JOY_AXIS_Y:
2402                 return &ji.dwYpos;
2403         case JOY_AXIS_Z:
2404                 return &ji.dwZpos;
2405         case JOY_AXIS_R:
2406                 return &ji.dwRpos;
2407         case JOY_AXIS_U:
2408                 return &ji.dwUpos;
2409         case JOY_AXIS_V:
2410                 return &ji.dwVpos;
2411         }
2412         return NULL; // LordHavoc: hush compiler warning
2413 }
2414
2415
2416 /*
2417 ===========
2418 Joy_AdvancedUpdate_f
2419 ===========
2420 */
2421 static void Joy_AdvancedUpdate_f (void)
2422 {
2423
2424         // called once by IN_ReadJoystick and by user whenever an update is needed
2425         // cvars are now available
2426         int     i;
2427         DWORD dwTemp;
2428
2429         // initialize all the maps
2430         for (i = 0; i < JOY_MAX_AXES; i++)
2431         {
2432                 dwAxisMap[i] = AxisNada;
2433                 dwControlMap[i] = JOY_ABSOLUTE_AXIS;
2434                 pdwRawValue[i] = RawValuePointer(i);
2435         }
2436
2437         if( joy_advanced.integer == 0)
2438         {
2439                 // default joystick initialization
2440                 // 2 axes only with joystick control
2441                 dwAxisMap[JOY_AXIS_X] = AxisTurn;
2442                 // dwControlMap[JOY_AXIS_X] = JOY_ABSOLUTE_AXIS;
2443                 dwAxisMap[JOY_AXIS_Y] = AxisForward;
2444                 // dwControlMap[JOY_AXIS_Y] = JOY_ABSOLUTE_AXIS;
2445         }
2446         else
2447         {
2448                 if (strcmp (joy_name.string, "joystick") != 0)
2449                 {
2450                         // notify user of advanced controller
2451                         Con_Printf("\n%s configured\n\n", joy_name.string);
2452                 }
2453
2454                 // advanced initialization here
2455                 // data supplied by user via joy_axisn cvars
2456                 dwTemp = (DWORD) joy_advaxisx.value;
2457                 dwAxisMap[JOY_AXIS_X] = dwTemp & 0x0000000f;
2458                 dwControlMap[JOY_AXIS_X] = dwTemp & JOY_RELATIVE_AXIS;
2459                 dwTemp = (DWORD) joy_advaxisy.value;
2460                 dwAxisMap[JOY_AXIS_Y] = dwTemp & 0x0000000f;
2461                 dwControlMap[JOY_AXIS_Y] = dwTemp & JOY_RELATIVE_AXIS;
2462                 dwTemp = (DWORD) joy_advaxisz.value;
2463                 dwAxisMap[JOY_AXIS_Z] = dwTemp & 0x0000000f;
2464                 dwControlMap[JOY_AXIS_Z] = dwTemp & JOY_RELATIVE_AXIS;
2465                 dwTemp = (DWORD) joy_advaxisr.value;
2466                 dwAxisMap[JOY_AXIS_R] = dwTemp & 0x0000000f;
2467                 dwControlMap[JOY_AXIS_R] = dwTemp & JOY_RELATIVE_AXIS;
2468                 dwTemp = (DWORD) joy_advaxisu.value;
2469                 dwAxisMap[JOY_AXIS_U] = dwTemp & 0x0000000f;
2470                 dwControlMap[JOY_AXIS_U] = dwTemp & JOY_RELATIVE_AXIS;
2471                 dwTemp = (DWORD) joy_advaxisv.value;
2472                 dwAxisMap[JOY_AXIS_V] = dwTemp & 0x0000000f;
2473                 dwControlMap[JOY_AXIS_V] = dwTemp & JOY_RELATIVE_AXIS;
2474         }
2475
2476         // compute the axes to collect from DirectInput
2477         joy_flags = JOY_RETURNCENTERED | JOY_RETURNBUTTONS | JOY_RETURNPOV;
2478         for (i = 0; i < JOY_MAX_AXES; i++)
2479         {
2480                 if (dwAxisMap[i] != AxisNada)
2481                 {
2482                         joy_flags |= dwAxisFlags[i];
2483                 }
2484         }
2485 }
2486
2487 /*
2488 ===============
2489 IN_ReadJoystick
2490 ===============
2491 */
2492 static qboolean IN_ReadJoystick (void)
2493 {
2494
2495         memset (&ji, 0, sizeof(ji));
2496         ji.dwSize = sizeof(ji);
2497         ji.dwFlags = joy_flags;
2498
2499         if (joyGetPosEx (joy_id, &ji) == JOYERR_NOERROR)
2500         {
2501                 // this is a hack -- there is a bug in the Logitech WingMan Warrior DirectInput Driver
2502                 // rather than having 32768 be the zero point, they have the zero point at 32668
2503                 // go figure -- anyway, now we get the full resolution out of the device
2504                 if (joy_wwhack1.integer != 0.0)
2505                 {
2506                         ji.dwUpos += 100;
2507                 }
2508                 return true;
2509         }
2510         else
2511         {
2512                 // read error occurred
2513                 // turning off the joystick seems too harsh for 1 read error,
2514                 // but what should be done?
2515                 return false;
2516         }
2517 }
2518
2519 /*
2520 ===========
2521  IN_JoystickGetAxisNum
2522 ===========
2523 */
2524
2525 int IN_JoystickGetAxisNum(int ControlListType)
2526 {
2527         int i;
2528
2529         for (i = 0; i < JOY_MAX_AXES; i++)
2530                 if (dwAxisMap[i] == (DWORD) ControlListType)
2531                         return i;
2532         return -1;
2533 }
2534
2535 /*
2536 ===========
2537  IN_JoystickGetAxis
2538 ===========
2539 */
2540 static double IN_JoystickGetAxis(int axis, double sensitivity, double deadzone)
2541 {
2542         float   fAxisValue, fTemp;
2543
2544         if (!joy_avail || axis < 0 || axis >= JOY_MAX_AXES)
2545                 return 0; // no such axis on this joystick
2546
2547         // get the floating point zero-centered, potentially-inverted data for the current axis
2548         fAxisValue = (float) *pdwRawValue[axis];
2549
2550         // move centerpoint to zero
2551         fAxisValue -= 32768.0;
2552
2553         if (joy_wwhack2.integer != 0.0)
2554         {
2555                 if (dwAxisMap[axis] == AxisTurn)
2556                 {
2557                         // this is a special formula for the Logitech WingMan Warrior
2558                         // y=ax^b; where a = 300 and b = 1.3
2559                         // also x values are in increments of 800 (so this is factored out)
2560                         // then bounds check result to level out excessively high spin rates
2561                         fTemp = 300.0 * pow(abs(fAxisValue) / 800.0, 1.3);
2562                         if (fTemp > 14000.0)
2563                                 fTemp = 14000.0;
2564                         // restore direction information
2565                         fAxisValue = (fAxisValue > 0.0) ? fTemp : -fTemp;
2566                 }
2567         }
2568
2569         // convert range from -32768..32767 to -1..1
2570         fAxisValue /= 32768.0;
2571
2572         // deadzone around center
2573         if (fabs(fAxisValue) < deadzone)
2574                 return 0; 
2575
2576         // apply sensitivity
2577         return fAxisValue * sensitivity;
2578 }
2579
2580 /*
2581 ===========
2582  IN_JoystickKeyeventForAxis
2583 ===========
2584 */
2585
2586 static void IN_JoystickKeyeventForAxis(int axis, int key_pos, int key_neg)
2587 {
2588         double joytime;
2589
2590         if (axis < 0 || axis >= JOY_MAX_AXES)
2591                 return; // no such axis on this joystick
2592
2593         joytime = Sys_DoubleTime();
2594         // no key event, continuous keydown event
2595         if (joy_axescache[axis].move == joy_axescache[axis].oldmove)
2596         {
2597                 if (joy_axescache[axis].move != 0 && joytime > joy_axescache[axis].keytime)
2598                 {
2599                         //Con_Printf("joy %s %i %f\n", Key_KeynumToString((joy_axescache[axis].move > 0) ? key_pos : key_neg), 1, cl.time);
2600                         Key_Event((joy_axescache[axis].move > 0) ? key_pos : key_neg, 0, 1);
2601                         joy_axescache[axis].keytime = joytime + 0.5 / 20;
2602                 }
2603                 return;
2604         }
2605         // generate key up event
2606         if (joy_axescache[axis].oldmove)
2607         {
2608                 //Con_Printf("joy %s %i %f\n", Key_KeynumToString((joy_axescache[axis].oldmove > 0) ? key_pos : key_neg), 1, cl.time);
2609                 Key_Event((joy_axescache[axis].oldmove > 0) ? key_pos : key_neg, 0, 0);
2610         }
2611         // generate key down event
2612         if (joy_axescache[axis].move)
2613         {
2614                 //Con_Printf("joy %s %i %f\n", Key_KeynumToString((joy_axescache[axis].move > 0) ? key_pos : key_neg), 1, cl.time);
2615                 Key_Event((joy_axescache[axis].move > 0) ? key_pos : key_neg, 0, 1);
2616                 joy_axescache[axis].keytime = joytime + 0.5;
2617         }
2618 }
2619
2620 /*
2621 ===========
2622  IN_JoystickBlockDoubledKeyEvents
2623 ===========
2624 */
2625
2626 static qboolean IN_ReadJoystick (void);
2627 static qboolean IN_JoystickBlockDoubledKeyEvents(int keycode)
2628 {
2629         int axis;
2630
2631         if (!joy_axiskeyevents.integer)
2632                 return false;
2633
2634         // block keyevent if it's going to be provided by joystick keyevent system
2635         if (joy_avail)
2636         {
2637                 // collect the joystick data, if possible
2638                 if (IN_ReadJoystick() != true)
2639                         return false;
2640                 axis = IN_JoystickGetAxisNum(AxisForward);
2641                 if (keycode == K_UPARROW || keycode == K_DOWNARROW)
2642                         if (IN_JoystickGetAxis(axis, 1, joy_axiskeyevents_deadzone.value) || joy_axescache[axis].move || joy_axescache[axis].oldmove)
2643                                 return true;
2644                 axis = IN_JoystickGetAxisNum(AxisSide);
2645                 if (keycode == K_RIGHTARROW || keycode == K_LEFTARROW)
2646                         if (IN_JoystickGetAxis(axis, 1, joy_axiskeyevents_deadzone.value) || joy_axescache[axis].move || joy_axescache[axis].oldmove)
2647                                 return true;
2648         }
2649
2650         return false;
2651 }
2652
2653 /*
2654 ===========
2655  IN_JoyMove
2656 ===========
2657 */
2658 static void IN_JoyMove (void)
2659 {
2660         float   speed, aspeed;
2661         float   fAxisValue;
2662         int             i, mouselook = (in_mlook.state & 1) || freelook.integer, AxisForwardIndex = -1, AxisSideIndex = -1;
2663
2664         // complete initialization if first time in
2665         // this is needed as cvars are not available at initialization time
2666         if( joy_advancedinit != true )
2667         {
2668                 Joy_AdvancedUpdate_f();
2669                 joy_advancedinit = true;
2670         }
2671
2672         if (joy_avail)
2673         {
2674                 int             i, key_index;
2675                 DWORD   buttonstate, povstate;
2676
2677                 // loop through the joystick buttons
2678                 // key a joystick event or auxillary event for higher number buttons for each state change
2679                 buttonstate = ji.dwButtons;
2680                 for (i=0 ; i < (int) joy_numbuttons ; i++)
2681                 {
2682                         if ( (buttonstate & (1<<i)) && !(joy_oldbuttonstate & (1<<i)) )
2683                         {
2684                                 key_index = (i < 16) ? K_JOY1 : K_AUX1;
2685                                 Key_Event (key_index + i, 0, true);
2686                         }
2687                         if ( !(buttonstate & (1<<i)) && (joy_oldbuttonstate & (1<<i)) )
2688                         {
2689                                 key_index = (i < 16) ? K_JOY1 : K_AUX1;
2690                                 Key_Event (key_index + i, 0, false);
2691                         }
2692                 }
2693                 joy_oldbuttonstate = buttonstate;
2694
2695                 if (joy_haspov)
2696                 {
2697                         // convert POV information into 4 bits of state information
2698                         // this avoids any potential problems related to moving from one
2699                         // direction to another without going through the center position
2700                         povstate = 0;
2701                         if(ji.dwPOV != JOY_POVCENTERED)
2702                         {
2703                                 if (ji.dwPOV == JOY_POVFORWARD)
2704                                         povstate |= 0x01;
2705                                 if (ji.dwPOV == JOY_POVRIGHT)
2706                                         povstate |= 0x02;
2707                                 if (ji.dwPOV == JOY_POVBACKWARD)
2708                                         povstate |= 0x04;
2709                                 if (ji.dwPOV == JOY_POVLEFT)
2710                                         povstate |= 0x08;
2711                         }
2712                         // determine which bits have changed and key an auxillary event for each change
2713                         for (i=0 ; i < 4 ; i++)
2714                         {
2715                                 if ( (povstate & (1<<i)) && !(joy_oldpovstate & (1<<i)) )
2716                                 {
2717                                         Key_Event (K_AUX29 + i, 0, true);
2718                                 }
2719
2720                                 if ( !(povstate & (1<<i)) && (joy_oldpovstate & (1<<i)) )
2721                                 {
2722                                         Key_Event (K_AUX29 + i, 0, false);
2723                                 }
2724                         }
2725                         joy_oldpovstate = povstate;
2726                 }
2727         }
2728
2729         // verify joystick is available and that the user wants to use it
2730         if (!joy_avail || !in_joystick.integer)
2731         {
2732                 return;
2733         }
2734
2735         // collect the joystick data, if possible
2736         if (IN_ReadJoystick () != true)
2737         {
2738                 return;
2739         }
2740
2741         if (in_speed.state & 1)
2742                 speed = cl_movespeedkey.value;
2743         else
2744                 speed = 1;
2745         // LordHavoc: viewzoom affects sensitivity for sniping
2746         aspeed = speed * cl.realframetime * cl.viewzoom;
2747
2748         // loop through the axes
2749         for (i = 0; i < JOY_MAX_AXES; i++)
2750         {
2751                 // convert axis to real move
2752                 switch (dwAxisMap[i])
2753                 {
2754                         case AxisForward:
2755                                 if (AxisForwardIndex < 0)
2756                                         AxisForwardIndex = i;
2757                                 if ((joy_advanced.integer == 0) && mouselook)
2758                                 {
2759                                         // user wants forward control to become look control
2760                                         fAxisValue = IN_JoystickGetAxis(i, joy_pitchsensitivity.value, joy_pitchthreshold.value);
2761                                         if (fAxisValue != 0)
2762                                         {
2763                                                 // if mouse invert is on, invert the joystick pitch value
2764                                                 // only absolute control support here (joy_advanced is false)
2765                                                 if (m_pitch.value < 0.0)
2766                                                         cl.viewangles[PITCH] -= fAxisValue * aspeed * cl_pitchspeed.value;
2767                                                 else
2768                                                         cl.viewangles[PITCH] += fAxisValue * aspeed * cl_pitchspeed.value;
2769                                                 V_StopPitchDrift();
2770                                         }
2771                                         else
2772                                         {
2773                                                 // no pitch movement
2774                                                 // disable pitch return-to-center unless requested by user
2775                                                 // *** this code can be removed when the lookspring bug is fixed
2776                                                 // *** the bug always has the lookspring feature on
2777                                                 if (lookspring.value == 0.0)
2778                                                         V_StopPitchDrift();
2779                                         }
2780                                 }
2781                                 else
2782                                 {
2783                                         // user wants forward control to be forward control
2784                                         fAxisValue = IN_JoystickGetAxis(i, joy_forwardsensitivity.value, joy_forwardthreshold.value);
2785                                         cl.cmd.forwardmove += fAxisValue * speed * cl_forwardspeed.value;
2786                                 }
2787                                 break;
2788
2789                         case AxisSide:
2790                                 if (AxisSideIndex < 0)
2791                                         AxisSideIndex = i;
2792                                 fAxisValue = IN_JoystickGetAxis(i, joy_sidesensitivity.value, joy_sidethreshold.value);
2793                                 cl.cmd.sidemove += fAxisValue * speed * cl_sidespeed.value;
2794                                 break;
2795
2796                         case AxisTurn:
2797                                 if ((in_strafe.state & 1) || (lookstrafe.integer && mouselook))
2798                                 {
2799                                         // user wants turn control to become side control
2800                                         fAxisValue = IN_JoystickGetAxis(i, joy_sidesensitivity.value, joy_sidethreshold.value);
2801                                         cl.cmd.sidemove -= fAxisValue * speed * cl_sidespeed.value;
2802                                 }
2803                                 else
2804                                 {
2805                                         // user wants turn control to be turn control
2806                                         fAxisValue = IN_JoystickGetAxis(i, joy_yawsensitivity.value, joy_yawthreshold.value);
2807                                         if (dwControlMap[i] == JOY_ABSOLUTE_AXIS)
2808                                                 cl.viewangles[YAW] += fAxisValue * aspeed * cl_yawspeed.value;
2809                                         else
2810                                                 cl.viewangles[YAW] += fAxisValue * speed * 180.0;
2811                                 }
2812                                 break;
2813
2814                         case AxisLook:
2815                                 fAxisValue = IN_JoystickGetAxis(i, joy_pitchsensitivity.value, joy_pitchthreshold.value);
2816                                 if (mouselook)
2817                                 {
2818                                         if (fAxisValue != 0)
2819                                         {
2820                                                 // pitch movement detected and pitch movement desired by user
2821                                                 if (dwControlMap[i] == JOY_ABSOLUTE_AXIS)
2822                                                         cl.viewangles[PITCH] += fAxisValue * aspeed * cl_pitchspeed.value;
2823                                                 else
2824                                                         cl.viewangles[PITCH] += fAxisValue * speed * 180.0;
2825                                                 V_StopPitchDrift();
2826                                         }
2827                                         else
2828                                         {
2829                                                 // no pitch movement
2830                                                 // disable pitch return-to-center unless requested by user
2831                                                 // *** this code can be removed when the lookspring bug is fixed
2832                                                 // *** the bug always has the lookspring feature on
2833                                                 if(lookspring.integer == 0)
2834                                                         V_StopPitchDrift();
2835                                         }
2836                                 }
2837                                 break;
2838
2839                         default:
2840                                 fAxisValue = IN_JoystickGetAxis(i, 1, 0.01);
2841                                 break;
2842                 }
2843         
2844                 // cache for keyevents
2845                 joy_axescache[i].oldmove = joy_axescache[i].move;
2846                 joy_axescache[i].move = IN_JoystickGetAxis(i, 1, joy_axiskeyevents_deadzone.value);
2847         }
2848
2849         // run keyevents
2850         if (joy_axiskeyevents.integer)
2851         {
2852                 IN_JoystickKeyeventForAxis(AxisForwardIndex, K_DOWNARROW, K_UPARROW);
2853                 IN_JoystickKeyeventForAxis(AxisSideIndex, K_RIGHTARROW, K_LEFTARROW);
2854         }
2855 }
2856
2857 static void IN_Init(void)
2858 {
2859         uiWheelMessage = RegisterWindowMessage ( "MSWHEEL_ROLLMSG" );
2860
2861         // joystick variables
2862         Cvar_RegisterVariable (&in_joystick);
2863         Cvar_RegisterVariable (&joy_name);
2864         Cvar_RegisterVariable (&joy_advanced);
2865         Cvar_RegisterVariable (&joy_advaxisx);
2866         Cvar_RegisterVariable (&joy_advaxisy);
2867         Cvar_RegisterVariable (&joy_advaxisz);
2868         Cvar_RegisterVariable (&joy_advaxisr);
2869         Cvar_RegisterVariable (&joy_advaxisu);
2870         Cvar_RegisterVariable (&joy_advaxisv);
2871         Cvar_RegisterVariable (&joy_forwardthreshold);
2872         Cvar_RegisterVariable (&joy_sidethreshold);
2873         Cvar_RegisterVariable (&joy_pitchthreshold);
2874         Cvar_RegisterVariable (&joy_yawthreshold);
2875         Cvar_RegisterVariable (&joy_forwardsensitivity);
2876         Cvar_RegisterVariable (&joy_sidesensitivity);
2877         Cvar_RegisterVariable (&joy_pitchsensitivity);
2878         Cvar_RegisterVariable (&joy_yawsensitivity);
2879         Cvar_RegisterVariable (&joy_wwhack1);
2880         Cvar_RegisterVariable (&joy_wwhack2);
2881         Cvar_RegisterVariable (&joy_axiskeyevents);
2882         Cvar_RegisterVariable (&joy_axiskeyevents_deadzone);
2883         Cvar_RegisterVariable (&vid_forcerefreshrate);
2884         Cmd_AddCommand ("joyadvancedupdate", Joy_AdvancedUpdate_f, "applies current joyadv* cvar settings to the joystick driver");
2885 }
2886
2887 static void IN_Shutdown(void)
2888 {
2889 #ifdef SUPPORTDIRECTX
2890         if (g_pMouse)
2891                 IDirectInputDevice_Release(g_pMouse);
2892         g_pMouse = NULL;
2893
2894         if (g_pdi)
2895                 IDirectInput_Release(g_pdi);
2896         g_pdi = NULL;
2897 #endif
2898 }
2899
2900 size_t VID_ListModes(vid_mode_t *modes, size_t maxcount)
2901 {
2902         int i;
2903         size_t k;
2904         DEVMODE thismode;
2905
2906         thismode.dmSize = sizeof(thismode);
2907         thismode.dmDriverExtra = 0;
2908         k = 0;
2909         for(i = 0; EnumDisplaySettings(NULL, i, &thismode); ++i)
2910         {
2911                 if(~thismode.dmFields & (DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY))
2912                 {
2913                         Con_DPrintf("enumerating modes yielded a bogus item... please debug this\n");
2914                         continue;
2915                 }
2916                 if(k >= maxcount)
2917                         break;
2918                 modes[k].width = thismode.dmPelsWidth;
2919                 modes[k].height = thismode.dmPelsHeight;
2920                 modes[k].bpp = thismode.dmBitsPerPel;
2921                 modes[k].refreshrate = thismode.dmDisplayFrequency;
2922                 modes[k].pixelheight_num = 1;
2923                 modes[k].pixelheight_denom = 1; // Win32 apparently does not provide this (FIXME)
2924                 ++k;
2925         }
2926         return k;
2927 }