WGL client can now use DPSOFTRAST, added thread_win.c to avoid SDL dependency for...
[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         vid.forcevbo = false;
1573         vid.support.arb_depth_texture = true;
1574         vid.support.arb_draw_buffers = vid_d3d9caps.NumSimultaneousRTs > 1;
1575         vid.support.arb_occlusion_query = true; // can't find a cap for this
1576         vid.support.arb_shadow = true;
1577         vid.support.arb_texture_compression = true;
1578         vid.support.arb_texture_cube_map = true;
1579         vid.support.arb_texture_non_power_of_two = (vid_d3d9caps.TextureCaps & D3DPTEXTURECAPS_POW2) == 0;
1580         vid.support.arb_vertex_buffer_object = true;
1581         vid.support.ext_blend_subtract = true;
1582         vid.support.ext_draw_range_elements = true;
1583         vid.support.ext_framebuffer_object = true;
1584         vid.support.ext_texture_3d = true;
1585         vid.support.ext_texture_compression_s3tc = true;
1586         vid.support.ext_texture_filter_anisotropic = true;
1587         vid.support.ati_separate_stencil = (vid_d3d9caps.StencilCaps & D3DSTENCILCAPS_TWOSIDED) != 0;
1588
1589         vid.maxtexturesize_2d = min(vid_d3d9caps.MaxTextureWidth, vid_d3d9caps.MaxTextureHeight);
1590         vid.maxtexturesize_3d = vid_d3d9caps.MaxVolumeExtent;
1591         vid.maxtexturesize_cubemap = vid.maxtexturesize_2d;
1592         vid.texunits = 4;
1593         vid.teximageunits = vid_d3d9caps.MaxSimultaneousTextures;
1594         vid.texarrayunits = 8; // can't find a caps field for this?
1595         vid.max_anisotropy = vid_d3d9caps.MaxAnisotropy;
1596         vid.maxdrawbuffers = vid_d3d9caps.NumSimultaneousRTs;
1597
1598         vid.texunits = bound(4, vid.texunits, MAX_TEXTUREUNITS);
1599         vid.teximageunits = bound(16, vid.teximageunits, MAX_TEXTUREUNITS);
1600         vid.texarrayunits = bound(8, vid.texarrayunits, MAX_TEXTUREUNITS);
1601         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)" : "");
1602         vid.renderpath = RENDERPATH_D3D9;
1603         vid.useinterleavedarrays = true;
1604
1605         Cvar_SetQuick(&gl_info_vendor, gl_vendor);
1606         Cvar_SetQuick(&gl_info_renderer, gl_renderer);
1607         Cvar_SetQuick(&gl_info_version, gl_version);
1608         Cvar_SetQuick(&gl_info_platform, gl_platform ? gl_platform : "");
1609         Cvar_SetQuick(&gl_info_driver, gl_driver);
1610
1611         // LordHavoc: report supported extensions
1612         Con_DPrintf("\nQuakeC extensions for server and client: %s\nQuakeC extensions for menu: %s\n", vm_sv_extensions, vm_m_extensions );
1613
1614         // clear to black (loading plaque will be seen over this)
1615         IDirect3DDevice9_Clear(vid_d3d9dev, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
1616         IDirect3DDevice9_BeginScene(vid_d3d9dev);
1617         IDirect3DDevice9_EndScene(vid_d3d9dev);
1618         IDirect3DDevice9_Present(vid_d3d9dev, NULL, NULL, NULL, NULL);
1619         // because the only time we end/begin scene is in VID_Finish, we'd better start a scene now...
1620         IDirect3DDevice9_BeginScene(vid_d3d9dev);
1621         vid_begunscene = true;
1622
1623         //vid_menudrawfn = VID_MenuDraw;
1624         //vid_menukeyfn = VID_MenuKey;
1625         vid_usingmouse = false;
1626         vid_usinghidecursor = false;
1627         vid_usingvsync = false;
1628         vid_hidden = vid_reallyhidden = false;
1629         vid_initialized = true;
1630
1631         IN_StartupMouse ();
1632         IN_StartupJoystick ();
1633
1634         return true;
1635 }
1636 #endif
1637
1638 qboolean VID_InitModeSOFT(viddef_mode_t *mode)
1639 {
1640         int i;
1641         HDC hdc;
1642         RECT rect;
1643         MSG msg;
1644         int pixelformat, newpixelformat;
1645         DWORD WindowStyle, ExWindowStyle;
1646         int CenterX, CenterY;
1647         int depth;
1648         DEVMODE thismode;
1649         qboolean foundmode, foundgoodmode;
1650         int bpp = mode->bitsperpixel;
1651         int width = mode->width;
1652         int height = mode->height;
1653         int refreshrate = (int)floor(mode->refreshrate+0.5);
1654         int fullscreen = mode->fullscreen;
1655
1656         if (vid_initialized)
1657                 Sys_Error("VID_InitMode called when video is already initialised");
1658
1659         memset(&gdevmode, 0, sizeof(gdevmode));
1660
1661         vid_isfullscreen = false;
1662         if (fullscreen)
1663         {
1664                 if(vid_forcerefreshrate.integer)
1665                 {
1666                         foundmode = true;
1667                         gdevmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
1668                         gdevmode.dmBitsPerPel = bpp;
1669                         gdevmode.dmPelsWidth = width;
1670                         gdevmode.dmPelsHeight = height;
1671                         gdevmode.dmSize = sizeof (gdevmode);
1672                         if(refreshrate)
1673                         {
1674                                 gdevmode.dmFields |= DM_DISPLAYFREQUENCY;
1675                                 gdevmode.dmDisplayFrequency = refreshrate;
1676                         }
1677                 }
1678                 else
1679                 {
1680                         if(refreshrate == 0)
1681                                 refreshrate = initialdevmode.dmDisplayFrequency; // default vid_refreshrate to the rate of the desktop
1682
1683                         foundmode = false;
1684                         foundgoodmode = false;
1685
1686                         thismode.dmSize = sizeof(thismode);
1687                         thismode.dmDriverExtra = 0;
1688                         for(i = 0; EnumDisplaySettings(NULL, i, &thismode); ++i)
1689                         {
1690                                 if(~thismode.dmFields & (DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY))
1691                                 {
1692                                         Con_DPrintf("enumerating modes yielded a bogus item... please debug this\n");
1693                                         continue;
1694                                 }
1695                                 if(developer_extra.integer)
1696                                         Con_DPrintf("Found mode %dx%dx%dbpp %dHz... ", (int)thismode.dmPelsWidth, (int)thismode.dmPelsHeight, (int)thismode.dmBitsPerPel, (int)thismode.dmDisplayFrequency);
1697                                 if(thismode.dmBitsPerPel != (DWORD)bpp)
1698                                 {
1699                                         if(developer_extra.integer)
1700                                                 Con_DPrintf("wrong bpp\n");
1701                                         continue;
1702                                 }
1703                                 if(thismode.dmPelsWidth != (DWORD)width)
1704                                 {
1705                                         if(developer_extra.integer)
1706                                                 Con_DPrintf("wrong width\n");
1707                                         continue;
1708                                 }
1709                                 if(thismode.dmPelsHeight != (DWORD)height)
1710                                 {
1711                                         if(developer_extra.integer)
1712                                                 Con_DPrintf("wrong height\n");
1713                                         continue;
1714                                 }
1715
1716                                 if(foundgoodmode)
1717                                 {
1718                                         // if we have a good mode, make sure this mode is better than the previous one, and allowed by the refreshrate
1719                                         if(thismode.dmDisplayFrequency > (DWORD)refreshrate)
1720                                         {
1721                                                 if(developer_extra.integer)
1722                                                         Con_DPrintf("too high refresh rate\n");
1723                                                 continue;
1724                                         }
1725                                         else if(thismode.dmDisplayFrequency <= gdevmode.dmDisplayFrequency)
1726                                         {
1727                                                 if(developer_extra.integer)
1728                                                         Con_DPrintf("doesn't beat previous best match (too low)\n");
1729                                                 continue;
1730                                         }
1731                                 }
1732                                 else if(foundmode)
1733                                 {
1734                                         // we do have one, but it isn't good... make sure it has a lower frequency than the previous one
1735                                         if(thismode.dmDisplayFrequency >= gdevmode.dmDisplayFrequency)
1736                                         {
1737                                                 if(developer_extra.integer)
1738                                                         Con_DPrintf("doesn't beat previous best match (too high)\n");
1739                                                 continue;
1740                                         }
1741                                 }
1742                                 // otherwise, take anything
1743
1744                                 memcpy(&gdevmode, &thismode, sizeof(gdevmode));
1745                                 if(thismode.dmDisplayFrequency <= (DWORD)refreshrate)
1746                                         foundgoodmode = true;
1747                                 else
1748                                 {
1749                                         if(developer_extra.integer)
1750                                                 Con_DPrintf("(out of range)\n");
1751                                 }
1752                                 foundmode = true;
1753                                 if(developer_extra.integer)
1754                                         Con_DPrintf("accepted\n");
1755                         }
1756                 }
1757
1758                 if (!foundmode)
1759                 {
1760                         VID_Shutdown();
1761                         Con_Printf("Unable to find the requested mode %dx%dx%dbpp\n", width, height, bpp);
1762                         return false;
1763                 }
1764                 else if(ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
1765                 {
1766                         VID_Shutdown();
1767                         Con_Printf("Unable to change to requested mode %dx%dx%dbpp\n", width, height, bpp);
1768                         return false;
1769                 }
1770
1771                 vid_isfullscreen = true;
1772                 WindowStyle = WS_POPUP;
1773                 ExWindowStyle = WS_EX_TOPMOST;
1774         }
1775         else
1776         {
1777                 hdc = GetDC (NULL);
1778                 i = GetDeviceCaps(hdc, RASTERCAPS);
1779                 depth = GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL);
1780                 ReleaseDC (NULL, hdc);
1781                 if (i & RC_PALETTE)
1782                 {
1783                         VID_Shutdown();
1784                         Con_Print("Can't run in non-RGB mode\n");
1785                         return false;
1786                 }
1787                 if (bpp > depth)
1788                 {
1789                         VID_Shutdown();
1790                         Con_Print("A higher desktop depth is required to run this video mode\n");
1791                         return false;
1792                 }
1793
1794                 WindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
1795                 ExWindowStyle = 0;
1796         }
1797
1798         rect.top = 0;
1799         rect.left = 0;
1800         rect.right = width;
1801         rect.bottom = height;
1802         AdjustWindowRectEx(&rect, WindowStyle, false, 0);
1803
1804         if (fullscreen)
1805         {
1806                 CenterX = 0;
1807                 CenterY = 0;
1808         }
1809         else
1810         {
1811                 CenterX = (GetSystemMetrics(SM_CXSCREEN) - (rect.right - rect.left)) / 2;
1812                 CenterY = (GetSystemMetrics(SM_CYSCREEN) - (rect.bottom - rect.top)) / 2;
1813         }
1814         CenterX = max(0, CenterX);
1815         CenterY = max(0, CenterY);
1816
1817         // x and y may be changed by WM_MOVE messages
1818         window_x = CenterX;
1819         window_y = CenterY;
1820         rect.left += CenterX;
1821         rect.right += CenterX;
1822         rect.top += CenterY;
1823         rect.bottom += CenterY;
1824
1825         pixelformat = 0;
1826         newpixelformat = 0;
1827         gl_extensions = "";
1828         gl_platformextensions = "";
1829
1830         mainwindow = CreateWindowEx (ExWindowStyle, "DarkPlacesWindowClass", gamename, WindowStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, global_hInstance, NULL);
1831         if (!mainwindow)
1832         {
1833                 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);
1834                 VID_Shutdown();
1835                 return false;
1836         }
1837
1838         baseDC = GetDC(mainwindow);
1839         vid.softpixels = NULL;
1840         memset(&vid_softbmi, 0, sizeof(vid_softbmi));
1841         vid_softbmi.bmiHeader.biSize = sizeof(vid_softbmi.bmiHeader);
1842         vid_softbmi.bmiHeader.biWidth = width;
1843         vid_softbmi.bmiHeader.biHeight = -height; // negative to make a top-down bitmap
1844         vid_softbmi.bmiHeader.biPlanes = 1;
1845         vid_softbmi.bmiHeader.biBitCount = 32;
1846         vid_softbmi.bmiHeader.biCompression = BI_RGB;
1847         vid_softbmi.bmiHeader.biSizeImage = width*height*4;
1848         vid_softbmi.bmiHeader.biClrUsed = 256;
1849         vid_softbmi.bmiHeader.biClrImportant = 256;
1850         vid_softdibhandle = CreateDIBSection(baseDC, &vid_softbmi, DIB_RGB_COLORS, (void **)&vid.softpixels, NULL, 0);
1851         if (!vid_softdibhandle)
1852         {
1853                 Con_Printf("CreateDIBSection failed\n");
1854                 VID_Shutdown();
1855                 return false;
1856         }
1857
1858         vid_softhdc = CreateCompatibleDC(baseDC);
1859         vid_softhdc_backup = SelectObject(vid_softhdc, vid_softdibhandle);
1860         if (!vid_softhdc_backup)
1861         {
1862                 Con_Printf("SelectObject failed\n");
1863                 VID_Shutdown();
1864                 return false;
1865         }
1866 //      ReleaseDC(mainwindow, baseDC);
1867 //      baseDC = NULL;
1868
1869         vid.softdepthpixels = (unsigned int *)calloc(1, mode->width * mode->height * 4);
1870         if (DPSOFTRAST_Init(mode->width, mode->height, vid_soft_threads.integer, vid_soft_interlace.integer, (unsigned int *)vid.softpixels, (unsigned int *)vid.softdepthpixels) < 0)
1871         {
1872                 Con_Printf("Failed to initialize software rasterizer\n");
1873                 VID_Shutdown();
1874                 return false;
1875         }
1876
1877         VID_Soft_SharedSetup();
1878
1879         ShowWindow (mainwindow, SW_SHOWDEFAULT);
1880         UpdateWindow (mainwindow);
1881
1882         // now we try to make sure we get the focus on the mode switch, because
1883         // sometimes in some systems we don't.  We grab the foreground, then
1884         // finish setting up, pump all our messages, and sleep for a little while
1885         // to let messages finish bouncing around the system, then we put
1886         // ourselves at the top of the z order, then grab the foreground again,
1887         // Who knows if it helps, but it probably doesn't hurt
1888         SetForegroundWindow (mainwindow);
1889
1890         while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
1891         {
1892                 TranslateMessage (&msg);
1893                 DispatchMessage (&msg);
1894         }
1895
1896         Sleep (100);
1897
1898         SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOCOPYBITS);
1899
1900         SetForegroundWindow (mainwindow);
1901
1902         // fix the leftover Alt from any Alt-Tab or the like that switched us away
1903         ClearAllStates ();
1904
1905         //vid_menudrawfn = VID_MenuDraw;
1906         //vid_menukeyfn = VID_MenuKey;
1907         vid_usingmouse = false;
1908         vid_usinghidecursor = false;
1909         vid_usingvsync = false;
1910         vid_reallyhidden = vid_hidden = false;
1911         vid_initialized = true;
1912
1913         IN_StartupMouse ();
1914         IN_StartupJoystick ();
1915
1916         return true;
1917 }
1918
1919 qboolean VID_InitMode(viddef_mode_t *mode)
1920 {
1921 #ifdef SSE_POSSIBLE
1922         if (vid_soft.integer)
1923                 return VID_InitModeSOFT(mode);
1924 #endif
1925 #ifdef SUPPORTD3D
1926 //      if (vid_dx11.integer)
1927 //              return VID_InitModeDX(mode, 11);
1928 //      if (vid_dx10.integer)
1929 //              return VID_InitModeDX(mode, 10);
1930         if (vid_dx9.integer)
1931                 return VID_InitModeDX(mode, 9);
1932 #endif
1933         return VID_InitModeGL(mode);
1934 }
1935
1936
1937 static void IN_Shutdown(void);
1938 void VID_Shutdown (void)
1939 {
1940         qboolean isgl;
1941         if(vid_initialized == false)
1942                 return;
1943
1944         VID_SetMouse(false, false, false);
1945         VID_RestoreSystemGamma();
1946
1947         vid_initialized = false;
1948         isgl = gldll != NULL;
1949         IN_Shutdown();
1950         gl_driver[0] = 0;
1951         gl_extensions = "";
1952         gl_platform = "";
1953         gl_platformextensions = "";
1954         if (vid_softhdc)
1955         {
1956                 SelectObject(vid_softhdc, vid_softhdc_backup);
1957                 ReleaseDC(mainwindow, vid_softhdc);
1958         }
1959         vid_softhdc = NULL;
1960         vid_softhdc_backup = NULL;
1961         if (vid_softdibhandle)
1962                 DeleteObject(vid_softdibhandle);
1963         vid_softdibhandle = NULL;
1964         vid.softpixels = NULL;
1965         if (vid.softdepthpixels)
1966                 free(vid.softdepthpixels);
1967         vid.softdepthpixels = NULL;
1968 #ifdef SUPPORTD3D
1969         if (vid_d3d9dev)
1970         {
1971                 if (vid_begunscene)
1972                         IDirect3DDevice9_EndScene(vid_d3d9dev);
1973                 vid_begunscene = false;
1974 //              Cmd_ExecuteString("r_texturestats", src_command);
1975 //              Cmd_ExecuteString("memlist", src_command);
1976                 IDirect3DDevice9_Release(vid_d3d9dev);
1977         }
1978         vid_d3d9dev = NULL;
1979         if (vid_d3d9)
1980                 IDirect3D9_Release(vid_d3d9);
1981         vid_d3d9 = NULL;
1982 #endif
1983         if (qwglMakeCurrent)
1984                 qwglMakeCurrent(NULL, NULL);
1985         qwglMakeCurrent = NULL;
1986         if (baseRC && qwglDeleteContext)
1987                 qwglDeleteContext(baseRC);
1988         qwglDeleteContext = NULL;
1989         // close the library before we get rid of the window
1990         GL_CloseLibrary();
1991         if (baseDC && mainwindow)
1992                 ReleaseDC(mainwindow, baseDC);
1993         baseDC = NULL;
1994         AppActivate(false, false);
1995         if (mainwindow)
1996                 DestroyWindow(mainwindow);
1997         mainwindow = 0;
1998         if (vid_isfullscreen && isgl)
1999                 ChangeDisplaySettings (NULL, 0);
2000         vid_isfullscreen = false;
2001 }
2002
2003 void VID_SetMouse(qboolean fullscreengrab, qboolean relative, qboolean hidecursor)
2004 {
2005         static qboolean restore_spi;
2006         static int originalmouseparms[3];
2007
2008         if (!mouseinitialized)
2009                 return;
2010
2011         if (relative)
2012         {
2013                 if (!vid_usingmouse)
2014                 {
2015                         vid_usingmouse = true;
2016                         cl_ignoremousemoves = 2;
2017 #ifdef SUPPORTDIRECTX
2018                         if (dinput && g_pMouse)
2019                         {
2020                                 IDirectInputDevice_Acquire(g_pMouse);
2021                                 dinput_acquired = true;
2022                         }
2023                         else
2024 #endif
2025                         {
2026                                 RECT window_rect;
2027                                 window_rect.left = window_x;
2028                                 window_rect.top = window_y;
2029                                 window_rect.right = window_x + vid.width;
2030                                 window_rect.bottom = window_y + vid.height;
2031
2032                                 // change mouse settings to turn off acceleration
2033 // COMMANDLINEOPTION: Windows GDI Input: -noforcemparms disables setting of mouse parameters (not used with -dinput, windows only)
2034                                 if (!COM_CheckParm ("-noforcemparms") && SystemParametersInfo (SPI_GETMOUSE, 0, originalmouseparms, 0))
2035                                 {
2036                                         int newmouseparms[3];
2037                                         newmouseparms[0] = 0; // threshold to double movement (only if accel level is >= 1)
2038                                         newmouseparms[1] = 0; // threshold to quadruple movement (only if accel level is >= 2)
2039                                         newmouseparms[2] = 0; // maximum level of acceleration (0 = off)
2040                                         restore_spi = SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0) != FALSE;
2041                                 }
2042                                 else
2043                                         restore_spi = false;
2044                                 SetCursorPos ((window_x + vid.width / 2), (window_y + vid.height / 2));
2045
2046                                 SetCapture (mainwindow);
2047                                 ClipCursor (&window_rect);
2048                         }
2049                 }
2050         }
2051         else
2052         {
2053                 if (vid_usingmouse)
2054                 {
2055                         vid_usingmouse = false;
2056                         cl_ignoremousemoves = 2;
2057 #ifdef SUPPORTDIRECTX
2058                         if (dinput_acquired)
2059                         {
2060                                 IDirectInputDevice_Unacquire(g_pMouse);
2061                                 dinput_acquired = false;
2062                         }
2063                         else
2064 #endif
2065                         {
2066                                 // restore system mouseparms if we changed them
2067                                 if (restore_spi)
2068                                         SystemParametersInfo (SPI_SETMOUSE, 0, originalmouseparms, 0);
2069                                 restore_spi = false;
2070                                 ClipCursor (NULL);
2071                                 ReleaseCapture ();
2072                         }
2073                 }
2074         }
2075
2076         if (vid_usinghidecursor != hidecursor)
2077         {
2078                 vid_usinghidecursor = hidecursor;
2079                 ShowCursor (!hidecursor);
2080         }
2081 }
2082
2083
2084 #ifdef SUPPORTDIRECTX
2085 /*
2086 ===========
2087 IN_InitDInput
2088 ===========
2089 */
2090 static qboolean IN_InitDInput (void)
2091 {
2092     HRESULT             hr;
2093         DIPROPDWORD     dipdw = {
2094                 {
2095                         sizeof(DIPROPDWORD),        // diph.dwSize
2096                         sizeof(DIPROPHEADER),       // diph.dwHeaderSize
2097                         0,                          // diph.dwObj
2098                         DIPH_DEVICE,                // diph.dwHow
2099                 },
2100                 DINPUT_BUFFERSIZE,              // dwData
2101         };
2102
2103         if (!hInstDI)
2104         {
2105                 hInstDI = LoadLibrary("dinput.dll");
2106
2107                 if (hInstDI == NULL)
2108                 {
2109                         Con_Print("Couldn't load dinput.dll\n");
2110                         return false;
2111                 }
2112         }
2113
2114         if (!pDirectInputCreate)
2115         {
2116                 pDirectInputCreate = (HRESULT (__stdcall *)(HINSTANCE,DWORD,LPDIRECTINPUT *,LPUNKNOWN))GetProcAddress(hInstDI,"DirectInputCreateA");
2117
2118                 if (!pDirectInputCreate)
2119                 {
2120                         Con_Print("Couldn't get DI proc addr\n");
2121                         return false;
2122                 }
2123         }
2124
2125 // register with DirectInput and get an IDirectInput to play with.
2126         hr = iDirectInputCreate(global_hInstance, DIRECTINPUT_VERSION, &g_pdi, NULL);
2127
2128         if (FAILED(hr))
2129         {
2130                 return false;
2131         }
2132
2133 // obtain an interface to the system mouse device.
2134 #ifdef __cplusplus
2135         hr = IDirectInput_CreateDevice(g_pdi, GUID_SysMouse, &g_pMouse, NULL);
2136 #else
2137         hr = IDirectInput_CreateDevice(g_pdi, &GUID_SysMouse, &g_pMouse, NULL);
2138 #endif
2139
2140         if (FAILED(hr))
2141         {
2142                 Con_Print("Couldn't open DI mouse device\n");
2143                 return false;
2144         }
2145
2146 // set the data format to "mouse format".
2147         hr = IDirectInputDevice_SetDataFormat(g_pMouse, &c_dfDIMouse);
2148
2149         if (FAILED(hr))
2150         {
2151                 Con_Print("Couldn't set DI mouse format\n");
2152                 return false;
2153         }
2154
2155 // set the cooperativity level.
2156         hr = IDirectInputDevice_SetCooperativeLevel(g_pMouse, mainwindow,
2157                         DISCL_EXCLUSIVE | DISCL_FOREGROUND);
2158
2159         if (FAILED(hr))
2160         {
2161                 Con_Print("Couldn't set DI coop level\n");
2162                 return false;
2163         }
2164
2165
2166 // set the buffer size to DINPUT_BUFFERSIZE elements.
2167 // the buffer size is a DWORD property associated with the device
2168         hr = IDirectInputDevice_SetProperty(g_pMouse, DIPROP_BUFFERSIZE, &dipdw.diph);
2169
2170         if (FAILED(hr))
2171         {
2172                 Con_Print("Couldn't set DI buffersize\n");
2173                 return false;
2174         }
2175
2176         return true;
2177 }
2178 #endif
2179
2180
2181 /*
2182 ===========
2183 IN_StartupMouse
2184 ===========
2185 */
2186 static void IN_StartupMouse (void)
2187 {
2188         if (COM_CheckParm ("-nomouse"))
2189                 return;
2190
2191         mouseinitialized = true;
2192
2193 #ifdef SUPPORTDIRECTX
2194 // COMMANDLINEOPTION: Windows Input: -dinput enables DirectInput for mouse/joystick input
2195         if (COM_CheckParm ("-dinput"))
2196                 dinput = IN_InitDInput ();
2197
2198         if (dinput)
2199                 Con_Print("DirectInput initialized\n");
2200         else
2201                 Con_Print("DirectInput not initialized\n");
2202 #endif
2203
2204         mouse_buttons = 10;
2205 }
2206
2207
2208 /*
2209 ===========
2210 IN_MouseMove
2211 ===========
2212 */
2213 static void IN_MouseMove (void)
2214 {
2215         POINT current_pos;
2216
2217         GetCursorPos (&current_pos);
2218         in_windowmouse_x = current_pos.x - window_x;
2219         in_windowmouse_y = current_pos.y - window_y;
2220
2221         if (!vid_usingmouse)
2222                 return;
2223
2224 #ifdef SUPPORTDIRECTX
2225         if (dinput_acquired)
2226         {
2227                 int i;
2228                 DIDEVICEOBJECTDATA      od;
2229                 DWORD                           dwElements;
2230                 HRESULT                         hr;
2231
2232                 for (;;)
2233                 {
2234                         dwElements = 1;
2235
2236                         hr = IDirectInputDevice_GetDeviceData(g_pMouse,
2237                                         sizeof(DIDEVICEOBJECTDATA), &od, &dwElements, 0);
2238
2239                         if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED))
2240                         {
2241                                 IDirectInputDevice_Acquire(g_pMouse);
2242                                 break;
2243                         }
2244
2245                         /* Unable to read data or no data available */
2246                         if (FAILED(hr) || dwElements == 0)
2247                                 break;
2248
2249                         /* Look at the element to see what happened */
2250
2251                         if ((int)od.dwOfs == DIMOFS_X)
2252                                 in_mouse_x += (LONG) od.dwData;
2253                         if ((int)od.dwOfs == DIMOFS_Y)
2254                                 in_mouse_y += (LONG) od.dwData;
2255                         if ((int)od.dwOfs == DIMOFS_Z)
2256                         {
2257                                 if((LONG)od.dwData < 0)
2258                                 {
2259                                         Key_Event(K_MWHEELDOWN, 0, true);
2260                                         Key_Event(K_MWHEELDOWN, 0, false);
2261                                 }
2262                                 else if((LONG)od.dwData > 0)
2263                                 {
2264                                         Key_Event(K_MWHEELUP, 0, true);
2265                                         Key_Event(K_MWHEELUP, 0, false);
2266                                 }
2267                         }
2268                         if ((int)od.dwOfs == DIMOFS_BUTTON0)
2269                                 mstate_di = (mstate_di & ~1) | ((od.dwData & 0x80) >> 7);
2270                         if ((int)od.dwOfs == DIMOFS_BUTTON1)
2271                                 mstate_di = (mstate_di & ~2) | ((od.dwData & 0x80) >> 6);
2272                         if ((int)od.dwOfs == DIMOFS_BUTTON2)
2273                                 mstate_di = (mstate_di & ~4) | ((od.dwData & 0x80) >> 5);
2274                         if ((int)od.dwOfs == DIMOFS_BUTTON3)
2275                                 mstate_di = (mstate_di & ~8) | ((od.dwData & 0x80) >> 4);
2276                 }
2277
2278                 // perform button actions
2279                 for (i=0 ; i<mouse_buttons && i < 16 ; i++)
2280                         if ((mstate_di ^ mouse_oldbuttonstate) & (1<<i))
2281                                 Key_Event (buttonremap[i], 0, (mstate_di & (1<<i)) != 0);
2282                 mouse_oldbuttonstate = mstate_di;
2283         }
2284         else
2285 #endif
2286         {
2287                 in_mouse_x += in_windowmouse_x - (int)(vid.width / 2);
2288                 in_mouse_y += in_windowmouse_y - (int)(vid.height / 2);
2289
2290                 // if the mouse has moved, force it to the center, so there's room to move
2291                 if (in_mouse_x || in_mouse_y)
2292                         SetCursorPos ((window_x + vid.width / 2), (window_y + vid.height / 2));
2293         }
2294 }
2295
2296
2297 /*
2298 ===========
2299 IN_Move
2300 ===========
2301 */
2302 void IN_Move (void)
2303 {
2304         if (vid_activewindow && !vid_reallyhidden)
2305         {
2306                 IN_MouseMove ();
2307                 IN_JoyMove ();
2308         }
2309 }
2310
2311
2312 /*
2313 ===============
2314 IN_StartupJoystick
2315 ===============
2316 */
2317 static void IN_StartupJoystick (void)
2318 {
2319         int                     numdevs;
2320         JOYCAPS         jc;
2321         MMRESULT        mmr;
2322         mmr = 0;
2323
2324         // assume no joystick
2325         joy_avail = false;
2326
2327         // abort startup if user requests no joystick
2328 // COMMANDLINEOPTION: Windows Input: -nojoy disables joystick support, may be a small speed increase
2329         if (COM_CheckParm ("-nojoy"))
2330                 return;
2331
2332         // verify joystick driver is present
2333         if ((numdevs = joyGetNumDevs ()) == 0)
2334         {
2335                 Con_Print("\njoystick not found -- driver not present\n\n");
2336                 return;
2337         }
2338
2339         // cycle through the joystick ids for the first valid one
2340         for (joy_id=0 ; joy_id<numdevs ; joy_id++)
2341         {
2342                 memset (&ji, 0, sizeof(ji));
2343                 ji.dwSize = sizeof(ji);
2344                 ji.dwFlags = JOY_RETURNCENTERED;
2345
2346                 if ((mmr = joyGetPosEx (joy_id, &ji)) == JOYERR_NOERROR)
2347                         break;
2348         }
2349
2350         // abort startup if we didn't find a valid joystick
2351         if (mmr != JOYERR_NOERROR)
2352         {
2353                 Con_Printf("\njoystick not found -- no valid joysticks (%x)\n\n", mmr);
2354                 return;
2355         }
2356
2357         // get the capabilities of the selected joystick
2358         // abort startup if command fails
2359         memset (&jc, 0, sizeof(jc));
2360         if ((mmr = joyGetDevCaps (joy_id, &jc, sizeof(jc))) != JOYERR_NOERROR)
2361         {
2362                 Con_Printf("\njoystick not found -- invalid joystick capabilities (%x)\n\n", mmr);
2363                 return;
2364         }
2365
2366         // save the joystick's number of buttons and POV status
2367         joy_numbuttons = jc.wNumButtons;
2368         joy_haspov = (jc.wCaps & JOYCAPS_HASPOV) != 0;
2369
2370         // old button and POV states default to no buttons pressed
2371         joy_oldbuttonstate = joy_oldpovstate = 0;
2372
2373         // mark the joystick as available and advanced initialization not completed
2374         // this is needed as cvars are not available during initialization
2375
2376         joy_avail = true;
2377         joy_advancedinit = false;
2378
2379         Con_Print("\njoystick detected\n\n");
2380 }
2381
2382
2383 /*
2384 ===========
2385 RawValuePointer
2386 ===========
2387 */
2388 static PDWORD RawValuePointer (int axis)
2389 {
2390         switch (axis)
2391         {
2392         case JOY_AXIS_X:
2393                 return &ji.dwXpos;
2394         case JOY_AXIS_Y:
2395                 return &ji.dwYpos;
2396         case JOY_AXIS_Z:
2397                 return &ji.dwZpos;
2398         case JOY_AXIS_R:
2399                 return &ji.dwRpos;
2400         case JOY_AXIS_U:
2401                 return &ji.dwUpos;
2402         case JOY_AXIS_V:
2403                 return &ji.dwVpos;
2404         }
2405         return NULL; // LordHavoc: hush compiler warning
2406 }
2407
2408
2409 /*
2410 ===========
2411 Joy_AdvancedUpdate_f
2412 ===========
2413 */
2414 static void Joy_AdvancedUpdate_f (void)
2415 {
2416
2417         // called once by IN_ReadJoystick and by user whenever an update is needed
2418         // cvars are now available
2419         int     i;
2420         DWORD dwTemp;
2421
2422         // initialize all the maps
2423         for (i = 0; i < JOY_MAX_AXES; i++)
2424         {
2425                 dwAxisMap[i] = AxisNada;
2426                 dwControlMap[i] = JOY_ABSOLUTE_AXIS;
2427                 pdwRawValue[i] = RawValuePointer(i);
2428         }
2429
2430         if( joy_advanced.integer == 0)
2431         {
2432                 // default joystick initialization
2433                 // 2 axes only with joystick control
2434                 dwAxisMap[JOY_AXIS_X] = AxisTurn;
2435                 // dwControlMap[JOY_AXIS_X] = JOY_ABSOLUTE_AXIS;
2436                 dwAxisMap[JOY_AXIS_Y] = AxisForward;
2437                 // dwControlMap[JOY_AXIS_Y] = JOY_ABSOLUTE_AXIS;
2438         }
2439         else
2440         {
2441                 if (strcmp (joy_name.string, "joystick") != 0)
2442                 {
2443                         // notify user of advanced controller
2444                         Con_Printf("\n%s configured\n\n", joy_name.string);
2445                 }
2446
2447                 // advanced initialization here
2448                 // data supplied by user via joy_axisn cvars
2449                 dwTemp = (DWORD) joy_advaxisx.value;
2450                 dwAxisMap[JOY_AXIS_X] = dwTemp & 0x0000000f;
2451                 dwControlMap[JOY_AXIS_X] = dwTemp & JOY_RELATIVE_AXIS;
2452                 dwTemp = (DWORD) joy_advaxisy.value;
2453                 dwAxisMap[JOY_AXIS_Y] = dwTemp & 0x0000000f;
2454                 dwControlMap[JOY_AXIS_Y] = dwTemp & JOY_RELATIVE_AXIS;
2455                 dwTemp = (DWORD) joy_advaxisz.value;
2456                 dwAxisMap[JOY_AXIS_Z] = dwTemp & 0x0000000f;
2457                 dwControlMap[JOY_AXIS_Z] = dwTemp & JOY_RELATIVE_AXIS;
2458                 dwTemp = (DWORD) joy_advaxisr.value;
2459                 dwAxisMap[JOY_AXIS_R] = dwTemp & 0x0000000f;
2460                 dwControlMap[JOY_AXIS_R] = dwTemp & JOY_RELATIVE_AXIS;
2461                 dwTemp = (DWORD) joy_advaxisu.value;
2462                 dwAxisMap[JOY_AXIS_U] = dwTemp & 0x0000000f;
2463                 dwControlMap[JOY_AXIS_U] = dwTemp & JOY_RELATIVE_AXIS;
2464                 dwTemp = (DWORD) joy_advaxisv.value;
2465                 dwAxisMap[JOY_AXIS_V] = dwTemp & 0x0000000f;
2466                 dwControlMap[JOY_AXIS_V] = dwTemp & JOY_RELATIVE_AXIS;
2467         }
2468
2469         // compute the axes to collect from DirectInput
2470         joy_flags = JOY_RETURNCENTERED | JOY_RETURNBUTTONS | JOY_RETURNPOV;
2471         for (i = 0; i < JOY_MAX_AXES; i++)
2472         {
2473                 if (dwAxisMap[i] != AxisNada)
2474                 {
2475                         joy_flags |= dwAxisFlags[i];
2476                 }
2477         }
2478 }
2479
2480 /*
2481 ===============
2482 IN_ReadJoystick
2483 ===============
2484 */
2485 static qboolean IN_ReadJoystick (void)
2486 {
2487
2488         memset (&ji, 0, sizeof(ji));
2489         ji.dwSize = sizeof(ji);
2490         ji.dwFlags = joy_flags;
2491
2492         if (joyGetPosEx (joy_id, &ji) == JOYERR_NOERROR)
2493         {
2494                 // this is a hack -- there is a bug in the Logitech WingMan Warrior DirectInput Driver
2495                 // rather than having 32768 be the zero point, they have the zero point at 32668
2496                 // go figure -- anyway, now we get the full resolution out of the device
2497                 if (joy_wwhack1.integer != 0.0)
2498                 {
2499                         ji.dwUpos += 100;
2500                 }
2501                 return true;
2502         }
2503         else
2504         {
2505                 // read error occurred
2506                 // turning off the joystick seems too harsh for 1 read error,
2507                 // but what should be done?
2508                 return false;
2509         }
2510 }
2511
2512 /*
2513 ===========
2514  IN_JoystickGetAxisNum
2515 ===========
2516 */
2517
2518 int IN_JoystickGetAxisNum(int ControlListType)
2519 {
2520         int i;
2521
2522         for (i = 0; i < JOY_MAX_AXES; i++)
2523                 if (dwAxisMap[i] == (DWORD) ControlListType)
2524                         return i;
2525         return -1;
2526 }
2527
2528 /*
2529 ===========
2530  IN_JoystickGetAxis
2531 ===========
2532 */
2533 static double IN_JoystickGetAxis(int axis, double sensitivity, double deadzone)
2534 {
2535         float   fAxisValue, fTemp;
2536
2537         if (!joy_avail || axis < 0 || axis >= JOY_MAX_AXES)
2538                 return 0; // no such axis on this joystick
2539
2540         // get the floating point zero-centered, potentially-inverted data for the current axis
2541         fAxisValue = (float) *pdwRawValue[axis];
2542
2543         // move centerpoint to zero
2544         fAxisValue -= 32768.0;
2545
2546         if (joy_wwhack2.integer != 0.0)
2547         {
2548                 if (dwAxisMap[axis] == AxisTurn)
2549                 {
2550                         // this is a special formula for the Logitech WingMan Warrior
2551                         // y=ax^b; where a = 300 and b = 1.3
2552                         // also x values are in increments of 800 (so this is factored out)
2553                         // then bounds check result to level out excessively high spin rates
2554                         fTemp = 300.0 * pow(abs(fAxisValue) / 800.0, 1.3);
2555                         if (fTemp > 14000.0)
2556                                 fTemp = 14000.0;
2557                         // restore direction information
2558                         fAxisValue = (fAxisValue > 0.0) ? fTemp : -fTemp;
2559                 }
2560         }
2561
2562         // convert range from -32768..32767 to -1..1
2563         fAxisValue /= 32768.0;
2564
2565         // deadzone around center
2566         if (fabs(fAxisValue) < deadzone)
2567                 return 0; 
2568
2569         // apply sensitivity
2570         return fAxisValue * sensitivity;
2571 }
2572
2573 /*
2574 ===========
2575  IN_JoystickKeyeventForAxis
2576 ===========
2577 */
2578
2579 static void IN_JoystickKeyeventForAxis(int axis, int key_pos, int key_neg)
2580 {
2581         double joytime;
2582
2583         if (axis < 0 || axis >= JOY_MAX_AXES)
2584                 return; // no such axis on this joystick
2585
2586         joytime = Sys_DoubleTime();
2587         // no key event, continuous keydown event
2588         if (joy_axescache[axis].move == joy_axescache[axis].oldmove)
2589         {
2590                 if (joy_axescache[axis].move != 0 && joytime > joy_axescache[axis].keytime)
2591                 {
2592                         //Con_Printf("joy %s %i %f\n", Key_KeynumToString((joy_axescache[axis].move > 0) ? key_pos : key_neg), 1, cl.time);
2593                         Key_Event((joy_axescache[axis].move > 0) ? key_pos : key_neg, 0, 1);
2594                         joy_axescache[axis].keytime = joytime + 0.5 / 20;
2595                 }
2596                 return;
2597         }
2598         // generate key up event
2599         if (joy_axescache[axis].oldmove)
2600         {
2601                 //Con_Printf("joy %s %i %f\n", Key_KeynumToString((joy_axescache[axis].oldmove > 0) ? key_pos : key_neg), 1, cl.time);
2602                 Key_Event((joy_axescache[axis].oldmove > 0) ? key_pos : key_neg, 0, 0);
2603         }
2604         // generate key down event
2605         if (joy_axescache[axis].move)
2606         {
2607                 //Con_Printf("joy %s %i %f\n", Key_KeynumToString((joy_axescache[axis].move > 0) ? key_pos : key_neg), 1, cl.time);
2608                 Key_Event((joy_axescache[axis].move > 0) ? key_pos : key_neg, 0, 1);
2609                 joy_axescache[axis].keytime = joytime + 0.5;
2610         }
2611 }
2612
2613 /*
2614 ===========
2615  IN_JoystickBlockDoubledKeyEvents
2616 ===========
2617 */
2618
2619 static qboolean IN_ReadJoystick (void);
2620 static qboolean IN_JoystickBlockDoubledKeyEvents(int keycode)
2621 {
2622         int axis;
2623
2624         if (!joy_axiskeyevents.integer)
2625                 return false;
2626
2627         // block keyevent if it's going to be provided by joystick keyevent system
2628         if (joy_avail)
2629         {
2630                 // collect the joystick data, if possible
2631                 if (IN_ReadJoystick() != true)
2632                         return false;
2633                 axis = IN_JoystickGetAxisNum(AxisForward);
2634                 if (keycode == K_UPARROW || keycode == K_DOWNARROW)
2635                         if (IN_JoystickGetAxis(axis, 1, joy_axiskeyevents_deadzone.value) || joy_axescache[axis].move || joy_axescache[axis].oldmove)
2636                                 return true;
2637                 axis = IN_JoystickGetAxisNum(AxisSide);
2638                 if (keycode == K_RIGHTARROW || keycode == K_LEFTARROW)
2639                         if (IN_JoystickGetAxis(axis, 1, joy_axiskeyevents_deadzone.value) || joy_axescache[axis].move || joy_axescache[axis].oldmove)
2640                                 return true;
2641         }
2642
2643         return false;
2644 }
2645
2646 /*
2647 ===========
2648  IN_JoyMove
2649 ===========
2650 */
2651 static void IN_JoyMove (void)
2652 {
2653         float   speed, aspeed;
2654         float   fAxisValue;
2655         int             i, mouselook = (in_mlook.state & 1) || freelook.integer, AxisForwardIndex = -1, AxisSideIndex = -1;
2656
2657         // complete initialization if first time in
2658         // this is needed as cvars are not available at initialization time
2659         if( joy_advancedinit != true )
2660         {
2661                 Joy_AdvancedUpdate_f();
2662                 joy_advancedinit = true;
2663         }
2664
2665         if (joy_avail)
2666         {
2667                 int             i, key_index;
2668                 DWORD   buttonstate, povstate;
2669
2670                 // loop through the joystick buttons
2671                 // key a joystick event or auxillary event for higher number buttons for each state change
2672                 buttonstate = ji.dwButtons;
2673                 for (i=0 ; i < (int) joy_numbuttons ; i++)
2674                 {
2675                         if ( (buttonstate & (1<<i)) && !(joy_oldbuttonstate & (1<<i)) )
2676                         {
2677                                 key_index = (i < 16) ? K_JOY1 : K_AUX1;
2678                                 Key_Event (key_index + i, 0, true);
2679                         }
2680                         if ( !(buttonstate & (1<<i)) && (joy_oldbuttonstate & (1<<i)) )
2681                         {
2682                                 key_index = (i < 16) ? K_JOY1 : K_AUX1;
2683                                 Key_Event (key_index + i, 0, false);
2684                         }
2685                 }
2686                 joy_oldbuttonstate = buttonstate;
2687
2688                 if (joy_haspov)
2689                 {
2690                         // convert POV information into 4 bits of state information
2691                         // this avoids any potential problems related to moving from one
2692                         // direction to another without going through the center position
2693                         povstate = 0;
2694                         if(ji.dwPOV != JOY_POVCENTERED)
2695                         {
2696                                 if (ji.dwPOV == JOY_POVFORWARD)
2697                                         povstate |= 0x01;
2698                                 if (ji.dwPOV == JOY_POVRIGHT)
2699                                         povstate |= 0x02;
2700                                 if (ji.dwPOV == JOY_POVBACKWARD)
2701                                         povstate |= 0x04;
2702                                 if (ji.dwPOV == JOY_POVLEFT)
2703                                         povstate |= 0x08;
2704                         }
2705                         // determine which bits have changed and key an auxillary event for each change
2706                         for (i=0 ; i < 4 ; i++)
2707                         {
2708                                 if ( (povstate & (1<<i)) && !(joy_oldpovstate & (1<<i)) )
2709                                 {
2710                                         Key_Event (K_AUX29 + i, 0, true);
2711                                 }
2712
2713                                 if ( !(povstate & (1<<i)) && (joy_oldpovstate & (1<<i)) )
2714                                 {
2715                                         Key_Event (K_AUX29 + i, 0, false);
2716                                 }
2717                         }
2718                         joy_oldpovstate = povstate;
2719                 }
2720         }
2721
2722         // verify joystick is available and that the user wants to use it
2723         if (!joy_avail || !in_joystick.integer)
2724         {
2725                 return;
2726         }
2727
2728         // collect the joystick data, if possible
2729         if (IN_ReadJoystick () != true)
2730         {
2731                 return;
2732         }
2733
2734         if (in_speed.state & 1)
2735                 speed = cl_movespeedkey.value;
2736         else
2737                 speed = 1;
2738         // LordHavoc: viewzoom affects sensitivity for sniping
2739         aspeed = speed * cl.realframetime * cl.viewzoom;
2740
2741         // loop through the axes
2742         for (i = 0; i < JOY_MAX_AXES; i++)
2743         {
2744                 // convert axis to real move
2745                 switch (dwAxisMap[i])
2746                 {
2747                         case AxisForward:
2748                                 if (AxisForwardIndex < 0)
2749                                         AxisForwardIndex = i;
2750                                 if ((joy_advanced.integer == 0) && mouselook)
2751                                 {
2752                                         // user wants forward control to become look control
2753                                         fAxisValue = IN_JoystickGetAxis(i, joy_pitchsensitivity.value, joy_pitchthreshold.value);
2754                                         if (fAxisValue != 0)
2755                                         {
2756                                                 // if mouse invert is on, invert the joystick pitch value
2757                                                 // only absolute control support here (joy_advanced is false)
2758                                                 if (m_pitch.value < 0.0)
2759                                                         cl.viewangles[PITCH] -= fAxisValue * aspeed * cl_pitchspeed.value;
2760                                                 else
2761                                                         cl.viewangles[PITCH] += fAxisValue * aspeed * cl_pitchspeed.value;
2762                                                 V_StopPitchDrift();
2763                                         }
2764                                         else
2765                                         {
2766                                                 // no pitch movement
2767                                                 // disable pitch return-to-center unless requested by user
2768                                                 // *** this code can be removed when the lookspring bug is fixed
2769                                                 // *** the bug always has the lookspring feature on
2770                                                 if (lookspring.value == 0.0)
2771                                                         V_StopPitchDrift();
2772                                         }
2773                                 }
2774                                 else
2775                                 {
2776                                         // user wants forward control to be forward control
2777                                         fAxisValue = IN_JoystickGetAxis(i, joy_forwardsensitivity.value, joy_forwardthreshold.value);
2778                                         cl.cmd.forwardmove += fAxisValue * speed * cl_forwardspeed.value;
2779                                 }
2780                                 break;
2781
2782                         case AxisSide:
2783                                 if (AxisSideIndex < 0)
2784                                         AxisSideIndex = i;
2785                                 fAxisValue = IN_JoystickGetAxis(i, joy_sidesensitivity.value, joy_sidethreshold.value);
2786                                 cl.cmd.sidemove += fAxisValue * speed * cl_sidespeed.value;
2787                                 break;
2788
2789                         case AxisTurn:
2790                                 if ((in_strafe.state & 1) || (lookstrafe.integer && mouselook))
2791                                 {
2792                                         // user wants turn control to become side control
2793                                         fAxisValue = IN_JoystickGetAxis(i, joy_sidesensitivity.value, joy_sidethreshold.value);
2794                                         cl.cmd.sidemove -= fAxisValue * speed * cl_sidespeed.value;
2795                                 }
2796                                 else
2797                                 {
2798                                         // user wants turn control to be turn control
2799                                         fAxisValue = IN_JoystickGetAxis(i, joy_yawsensitivity.value, joy_yawthreshold.value);
2800                                         if (dwControlMap[i] == JOY_ABSOLUTE_AXIS)
2801                                                 cl.viewangles[YAW] += fAxisValue * aspeed * cl_yawspeed.value;
2802                                         else
2803                                                 cl.viewangles[YAW] += fAxisValue * speed * 180.0;
2804                                 }
2805                                 break;
2806
2807                         case AxisLook:
2808                                 fAxisValue = IN_JoystickGetAxis(i, joy_pitchsensitivity.value, joy_pitchthreshold.value);
2809                                 if (mouselook)
2810                                 {
2811                                         if (fAxisValue != 0)
2812                                         {
2813                                                 // pitch movement detected and pitch movement desired by user
2814                                                 if (dwControlMap[i] == JOY_ABSOLUTE_AXIS)
2815                                                         cl.viewangles[PITCH] += fAxisValue * aspeed * cl_pitchspeed.value;
2816                                                 else
2817                                                         cl.viewangles[PITCH] += fAxisValue * speed * 180.0;
2818                                                 V_StopPitchDrift();
2819                                         }
2820                                         else
2821                                         {
2822                                                 // no pitch movement
2823                                                 // disable pitch return-to-center unless requested by user
2824                                                 // *** this code can be removed when the lookspring bug is fixed
2825                                                 // *** the bug always has the lookspring feature on
2826                                                 if(lookspring.integer == 0)
2827                                                         V_StopPitchDrift();
2828                                         }
2829                                 }
2830                                 break;
2831
2832                         default:
2833                                 fAxisValue = IN_JoystickGetAxis(i, 1, 0.01);
2834                                 break;
2835                 }
2836         
2837                 // cache for keyevents
2838                 joy_axescache[i].oldmove = joy_axescache[i].move;
2839                 joy_axescache[i].move = IN_JoystickGetAxis(i, 1, joy_axiskeyevents_deadzone.value);
2840         }
2841
2842         // run keyevents
2843         if (joy_axiskeyevents.integer)
2844         {
2845                 IN_JoystickKeyeventForAxis(AxisForwardIndex, K_DOWNARROW, K_UPARROW);
2846                 IN_JoystickKeyeventForAxis(AxisSideIndex, K_RIGHTARROW, K_LEFTARROW);
2847         }
2848 }
2849
2850 static void IN_Init(void)
2851 {
2852         uiWheelMessage = RegisterWindowMessage ( "MSWHEEL_ROLLMSG" );
2853
2854         // joystick variables
2855         Cvar_RegisterVariable (&in_joystick);
2856         Cvar_RegisterVariable (&joy_name);
2857         Cvar_RegisterVariable (&joy_advanced);
2858         Cvar_RegisterVariable (&joy_advaxisx);
2859         Cvar_RegisterVariable (&joy_advaxisy);
2860         Cvar_RegisterVariable (&joy_advaxisz);
2861         Cvar_RegisterVariable (&joy_advaxisr);
2862         Cvar_RegisterVariable (&joy_advaxisu);
2863         Cvar_RegisterVariable (&joy_advaxisv);
2864         Cvar_RegisterVariable (&joy_forwardthreshold);
2865         Cvar_RegisterVariable (&joy_sidethreshold);
2866         Cvar_RegisterVariable (&joy_pitchthreshold);
2867         Cvar_RegisterVariable (&joy_yawthreshold);
2868         Cvar_RegisterVariable (&joy_forwardsensitivity);
2869         Cvar_RegisterVariable (&joy_sidesensitivity);
2870         Cvar_RegisterVariable (&joy_pitchsensitivity);
2871         Cvar_RegisterVariable (&joy_yawsensitivity);
2872         Cvar_RegisterVariable (&joy_wwhack1);
2873         Cvar_RegisterVariable (&joy_wwhack2);
2874         Cvar_RegisterVariable (&joy_axiskeyevents);
2875         Cvar_RegisterVariable (&joy_axiskeyevents_deadzone);
2876         Cvar_RegisterVariable (&vid_forcerefreshrate);
2877         Cmd_AddCommand ("joyadvancedupdate", Joy_AdvancedUpdate_f, "applies current joyadv* cvar settings to the joystick driver");
2878 }
2879
2880 static void IN_Shutdown(void)
2881 {
2882 #ifdef SUPPORTDIRECTX
2883         if (g_pMouse)
2884                 IDirectInputDevice_Release(g_pMouse);
2885         g_pMouse = NULL;
2886
2887         if (g_pdi)
2888                 IDirectInput_Release(g_pdi);
2889         g_pdi = NULL;
2890 #endif
2891 }
2892
2893 size_t VID_ListModes(vid_mode_t *modes, size_t maxcount)
2894 {
2895         int i;
2896         size_t k;
2897         DEVMODE thismode;
2898
2899         thismode.dmSize = sizeof(thismode);
2900         thismode.dmDriverExtra = 0;
2901         k = 0;
2902         for(i = 0; EnumDisplaySettings(NULL, i, &thismode); ++i)
2903         {
2904                 if(~thismode.dmFields & (DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY))
2905                 {
2906                         Con_DPrintf("enumerating modes yielded a bogus item... please debug this\n");
2907                         continue;
2908                 }
2909                 if(k >= maxcount)
2910                         break;
2911                 modes[k].width = thismode.dmPelsWidth;
2912                 modes[k].height = thismode.dmPelsHeight;
2913                 modes[k].bpp = thismode.dmBitsPerPel;
2914                 modes[k].refreshrate = thismode.dmDisplayFrequency;
2915                 modes[k].pixelheight_num = 1;
2916                 modes[k].pixelheight_denom = 1; // Win32 apparently does not provide this (FIXME)
2917                 ++k;
2918         }
2919         return k;
2920 }