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