]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - vid_wgl.c
Default vid_desktopfullscreen to 1.
[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
49 extern HINSTANCE global_hInstance;
50
51 static HINSTANCE gldll;
52
53 #ifndef WM_MOUSEWHEEL
54 #define WM_MOUSEWHEEL                   0x020A
55 #endif
56
57 // Tell startup code that we have a client
58 int cl_available = true;
59
60 qboolean vid_supportrefreshrate = true;
61
62 static int (WINAPI *qwglChoosePixelFormat)(HDC, CONST PIXELFORMATDESCRIPTOR *);
63 static int (WINAPI *qwglDescribePixelFormat)(HDC, int, UINT, LPPIXELFORMATDESCRIPTOR);
64 //static int (WINAPI *qwglGetPixelFormat)(HDC);
65 static BOOL (WINAPI *qwglSetPixelFormat)(HDC, int, CONST PIXELFORMATDESCRIPTOR *);
66 static BOOL (WINAPI *qwglSwapBuffers)(HDC);
67 static HGLRC (WINAPI *qwglCreateContext)(HDC);
68 static BOOL (WINAPI *qwglDeleteContext)(HGLRC);
69 static HGLRC (WINAPI *qwglGetCurrentContext)(VOID);
70 static HDC (WINAPI *qwglGetCurrentDC)(VOID);
71 static PROC (WINAPI *qwglGetProcAddress)(LPCSTR);
72 static BOOL (WINAPI *qwglMakeCurrent)(HDC, HGLRC);
73 static BOOL (WINAPI *qwglSwapIntervalEXT)(int interval);
74 static const char *(WINAPI *qwglGetExtensionsStringARB)(HDC hdc);
75 static BOOL (WINAPI *qwglChoosePixelFormatARB)(HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
76 static BOOL (WINAPI *qwglGetPixelFormatAttribivARB)(HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, int *piValues);
77
78 static dllfunction_t wglfuncs[] =
79 {
80         {"wglChoosePixelFormat", (void **) &qwglChoosePixelFormat},
81         {"wglDescribePixelFormat", (void **) &qwglDescribePixelFormat},
82 //      {"wglGetPixelFormat", (void **) &qwglGetPixelFormat},
83         {"wglSetPixelFormat", (void **) &qwglSetPixelFormat},
84         {"wglSwapBuffers", (void **) &qwglSwapBuffers},
85         {"wglCreateContext", (void **) &qwglCreateContext},
86         {"wglDeleteContext", (void **) &qwglDeleteContext},
87         {"wglGetProcAddress", (void **) &qwglGetProcAddress},
88         {"wglMakeCurrent", (void **) &qwglMakeCurrent},
89         {"wglGetCurrentContext", (void **) &qwglGetCurrentContext},
90         {"wglGetCurrentDC", (void **) &qwglGetCurrentDC},
91         {NULL, NULL}
92 };
93
94 static dllfunction_t wglswapintervalfuncs[] =
95 {
96         {"wglSwapIntervalEXT", (void **) &qwglSwapIntervalEXT},
97         {NULL, NULL}
98 };
99
100 static dllfunction_t wglpixelformatfuncs[] =
101 {
102         {"wglChoosePixelFormatARB", (void **) &qwglChoosePixelFormatARB},
103         {"wglGetPixelFormatAttribivARB", (void **) &qwglGetPixelFormatAttribivARB},
104         {NULL, NULL}
105 };
106
107 static DEVMODE gdevmode, initialdevmode;
108 static vid_mode_t desktop_mode;
109 static qboolean vid_initialized = false;
110 static qboolean vid_wassuspended = false;
111 static qboolean vid_usingmouse = false;
112 static qboolean vid_usinghidecursor = false;
113 static qboolean vid_usingvsync = false;
114 static qboolean vid_usevsync = false;
115 static HICON hIcon;
116
117 // used by cd_win.c and snd_win.c
118 HWND mainwindow;
119
120 static HDC       baseDC;
121 static HGLRC baseRC;
122
123 //HWND WINAPI InitializeWindow (HINSTANCE hInstance, int nCmdShow);
124
125 static qboolean vid_isfullscreen;
126
127 //void VID_MenuDraw (void);
128 //void VID_MenuKey (int key);
129
130 LONG WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
131 void AppActivate(BOOL fActive, BOOL minimize);
132 static void ClearAllStates(void);
133 qboolean VID_InitModeGL(viddef_mode_t *mode);
134 qboolean VID_InitModeSOFT(viddef_mode_t *mode);
135
136 //====================================
137
138 static int window_x, window_y;
139
140 static qboolean mouseinitialized;
141
142 #ifdef SUPPORTDIRECTX
143 static qboolean dinput;
144 #define DINPUT_BUFFERSIZE           16
145 #define iDirectInputCreate(a,b,c,d)     pDirectInputCreate(a,b,c,d)
146
147 static HRESULT (WINAPI *pDirectInputCreate)(HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUT * lplpDirectInput, LPUNKNOWN punkOuter);
148 #endif
149
150 // LordHavoc: thanks to backslash for this support for mouse buttons 4 and 5
151 /* backslash :: imouse explorer buttons */
152 /* These are #ifdefed out for non-Win2K in the February 2001 version of
153    MS's platform SDK, but we need them for compilation. . . */
154 #ifndef WM_XBUTTONDOWN
155    #define WM_XBUTTONDOWN      0x020B
156    #define WM_XBUTTONUP      0x020C
157 #endif
158 #ifndef MK_XBUTTON1
159    #define MK_XBUTTON1         0x0020
160    #define MK_XBUTTON2         0x0040
161 #endif
162 #ifndef MK_XBUTTON3
163 // LordHavoc: lets hope this allows more buttons in the future...
164    #define MK_XBUTTON3         0x0080
165    #define MK_XBUTTON4         0x0100
166    #define MK_XBUTTON5         0x0200
167    #define MK_XBUTTON6         0x0400
168    #define MK_XBUTTON7         0x0800
169 #endif
170 /* :: backslash */
171
172 // mouse variables
173 static int                      mouse_buttons;
174 static int                      mouse_oldbuttonstate;
175
176 static unsigned int uiWheelMessage;
177 #ifdef SUPPORTDIRECTX
178 static qboolean dinput_acquired;
179
180 static unsigned int             mstate_di;
181 #endif
182
183 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"};
184
185 #ifdef SUPPORTDIRECTX
186 static LPDIRECTINPUT            g_pdi;
187 static LPDIRECTINPUTDEVICE      g_pMouse;
188 static HINSTANCE hInstDI;
189 #endif
190
191 // forward-referenced functions
192 static void IN_StartupMouse (void);
193 static void AdjustWindowBounds(int fullscreen, int *width, int *height, viddef_mode_t *mode, DWORD WindowStyle, RECT *rect);
194
195 //====================================
196
197 qboolean vid_reallyhidden = true;
198 void VID_Finish (void)
199 {
200         vid_hidden = vid_reallyhidden;
201
202         vid_usevsync = vid_vsync.integer && !cls.timedemo && qwglSwapIntervalEXT;
203
204         if (!vid_hidden)
205         {
206                 switch(vid.renderpath)
207                 {
208                 case RENDERPATH_GL20:
209                 case RENDERPATH_GLES2:
210                         if (vid_usingvsync != vid_usevsync)
211                         {
212                                 vid_usingvsync = vid_usevsync;
213                                 qwglSwapIntervalEXT (vid_usevsync);
214                         }
215                         if (r_speeds.integer == 2 || gl_finish.integer)
216                                 GL_Finish();
217                         SwapBuffers(baseDC);
218                         break;
219                 }
220         }
221
222         // make sure a context switch can happen every frame - Logitech drivers
223         // input drivers sometimes eat cpu time every 3 seconds or lag badly
224         // without this help
225         Sleep(0);
226
227         VID_UpdateGamma();
228 }
229
230 //==========================================================================
231
232
233 static unsigned char scantokey[128] =
234 {
235 //  0           1        2     3     4     5       6           7      8         9      A          B           C       D            E           F
236         0          ,K_ESCAPE,'1'  ,'2'  ,'3'  ,'4'    ,'5'        ,'6'   ,'7'      ,'8'   ,'9'       ,'0'        ,'-'    ,'='         ,K_BACKSPACE,K_TAB,//0
237         'q'        ,'w'     ,'e'  ,'r'  ,'t'  ,'y'    ,'u'        ,'i'   ,'o'      ,'p'   ,'['       ,']'        ,K_ENTER,K_CTRL      ,'a'        ,'s'  ,//1
238         'd'        ,'f'     ,'g'  ,'h'  ,'j'  ,'k'    ,'l'        ,';'   ,'\''     ,'`'   ,K_SHIFT   ,'\\'       ,'z'    ,'x'         ,'c'        ,'v'  ,//2
239         'b'        ,'n'     ,'m'  ,','  ,'.'  ,'/'    ,K_SHIFT    ,'*'   ,K_ALT    ,' '   ,K_CAPSLOCK,K_F1       ,K_F2   ,K_F3        ,K_F4       ,K_F5 ,//3
240         K_F6       ,K_F7    ,K_F8 ,K_F9 ,K_F10,K_PAUSE,K_SCROLLOCK,K_HOME,K_UPARROW,K_PGUP,K_KP_MINUS,K_LEFTARROW,K_KP_5 ,K_RIGHTARROW,K_KP_PLUS  ,K_END,//4
241         K_DOWNARROW,K_PGDN  ,K_INS,K_DEL,0    ,0      ,0          ,K_F11 ,K_F12    ,0     ,0         ,0          ,0      ,0           ,0          ,0    ,//5
242         0          ,0       ,0    ,0    ,0    ,0      ,0          ,0     ,0        ,0     ,0         ,0          ,0      ,0           ,0          ,0    ,//6
243         0          ,0       ,0    ,0    ,0    ,0      ,0          ,0     ,0        ,0     ,0         ,0          ,0      ,0           ,0          ,0     //7
244 };
245
246
247 /*
248 =======
249 MapKey
250
251 Map from windows to quake keynums
252 =======
253 */
254 static int MapKey (int key, int virtualkey)
255 {
256         int result;
257         int modified = (key >> 16) & 255;
258         qboolean is_extended = false;
259
260         if (modified < 128 && scantokey[modified])
261                 result = scantokey[modified];
262         else
263         {
264                 result = 0;
265                 Con_DPrintf("key 0x%02x (0x%8x, 0x%8x) has no translation\n", modified, key, virtualkey);
266         }
267
268         if (key & (1 << 24))
269                 is_extended = true;
270
271         if ( !is_extended )
272         {
273                 if(((GetKeyState(VK_NUMLOCK)) & 0xffff) == 0)
274                         return result;
275
276                 switch ( result )
277                 {
278                 case K_HOME:
279                         return K_KP_HOME;
280                 case K_UPARROW:
281                         return K_KP_UPARROW;
282                 case K_PGUP:
283                         return K_KP_PGUP;
284                 case K_LEFTARROW:
285                         return K_KP_LEFTARROW;
286                 case K_RIGHTARROW:
287                         return K_KP_RIGHTARROW;
288                 case K_END:
289                         return K_KP_END;
290                 case K_DOWNARROW:
291                         return K_KP_DOWNARROW;
292                 case K_PGDN:
293                         return K_KP_PGDN;
294                 case K_INS:
295                         return K_KP_INS;
296                 case K_DEL:
297                         return K_KP_DEL;
298                 default:
299                         return result;
300                 }
301         }
302         else
303         {
304                 if(virtualkey == VK_NUMLOCK)
305                         return K_NUMLOCK;
306
307                 switch ( result )
308                 {
309                 case 0x0D:
310                         return K_KP_ENTER;
311                 case 0x2F:
312                         return K_KP_SLASH;
313                 case 0xAF:
314                         return K_KP_PLUS;
315                 }
316                 return result;
317         }
318 }
319
320 /*
321 ===================================================================
322
323 MAIN WINDOW
324
325 ===================================================================
326 */
327
328 /*
329 ================
330 ClearAllStates
331 ================
332 */
333 static void ClearAllStates (void)
334 {
335         Key_ReleaseAll();
336         if (vid_usingmouse)
337                 mouse_oldbuttonstate = 0;
338 }
339
340 void AppActivate(BOOL fActive, BOOL minimize)
341 /****************************************************************************
342 *
343 * Function:     AppActivate
344 * Parameters:   fActive - True if app is activating
345 *
346 * Description:  If the application is activating, then swap the system
347 *               into SYSPAL_NOSTATIC mode so that our palettes will display
348 *               correctly.
349 *
350 ****************************************************************************/
351 {
352         static qboolean sound_active = false;  // initially blocked by Sys_InitConsole()
353
354         vid_activewindow = fActive != FALSE;
355         vid_reallyhidden = minimize != FALSE;
356
357         // enable/disable sound on focus gain/loss
358         if ((!vid_reallyhidden && vid_activewindow) || !snd_mutewhenidle.integer)
359         {
360                 if (!sound_active)
361                 {
362                         S_UnblockSound ();
363                         sound_active = true;
364                 }
365         }
366         else
367         {
368                 if (sound_active)
369                 {
370                         S_BlockSound ();
371                         sound_active = false;
372                 }
373         }
374
375         if (fActive)
376         {
377                 if (vid_isfullscreen)
378                 {
379                         if (vid_wassuspended)
380                         {
381                                 vid_wassuspended = false;
382                                 if (gldll)
383                                 {
384                                         ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN);
385                                         ShowWindow(mainwindow, SW_SHOWNORMAL);
386                                 }
387                         }
388
389                         // LordHavoc: from dabb, fix for alt-tab bug in NVidia drivers
390                         if (gldll)
391                                 MoveWindow(mainwindow,0,0,gdevmode.dmPelsWidth,gdevmode.dmPelsHeight,false);
392                 }
393         }
394
395         if (!fActive)
396         {
397                 VID_SetMouse(false, false, false);
398                 if (vid_isfullscreen)
399                 {
400                         if (gldll)
401                                 ChangeDisplaySettings (NULL, CDS_FULLSCREEN);
402                         vid_wassuspended = true;
403                 }
404         }
405 }
406
407 //TODO: move it around in vid_wgl.c since I dont think this is the right position
408 void Sys_SendKeyEvents (void)
409 {
410         MSG msg;
411
412         while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
413         {
414                 if (!GetMessage (&msg, NULL, 0, 0))
415                         Sys_Quit (1);
416
417                 TranslateMessage (&msg);
418                 DispatchMessage (&msg);
419         }
420 }
421
422 #ifdef CONFIG_CD
423 LONG CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
424 #endif
425
426 static keynum_t buttonremap[16] =
427 {
428         K_MOUSE1,
429         K_MOUSE2,
430         K_MOUSE3,
431         K_MOUSE4,
432         K_MOUSE5,
433         K_MOUSE6,
434         K_MOUSE7,
435         K_MOUSE8,
436         K_MOUSE9,
437         K_MOUSE10,
438         K_MOUSE11,
439         K_MOUSE12,
440         K_MOUSE13,
441         K_MOUSE14,
442         K_MOUSE15,
443         K_MOUSE16,
444 };
445
446 /* main window procedure */
447 LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM  wParam, LPARAM lParam)
448 {
449         LONG    lRet = 1;
450         int             fActive, fMinimized, temp;
451         unsigned char state[256];
452         const unsigned int UNICODE_BUFFER_LENGTH = 4;
453         WCHAR unicode[UNICODE_BUFFER_LENGTH];
454         int             vkey;
455         int             charlength;
456         qboolean down = false;
457
458         if ( uMsg == uiWheelMessage )
459                 uMsg = WM_MOUSEWHEEL;
460
461         switch (uMsg)
462         {
463                 case WM_KILLFOCUS:
464                         if (vid_isfullscreen)
465                                 ShowWindow(mainwindow, SW_SHOWMINNOACTIVE);
466                         break;
467
468                 case WM_CREATE:
469                         break;
470
471                 case WM_MOVE:
472                         window_x = (short) LOWORD(lParam);
473                         window_y = (short) HIWORD(lParam);
474                         VID_SetMouse(false, false, false);
475                         break;
476
477                 case WM_KEYDOWN:
478                 case WM_SYSKEYDOWN:
479                         down = true;
480                 case WM_KEYUP:
481                 case WM_SYSKEYUP:
482                         vkey = MapKey(lParam, wParam);
483                         GetKeyboardState (state);
484                         // alt/ctrl/shift tend to produce funky ToAscii values,
485                         // and if it's not a single character we don't know care about it
486                         charlength = ToUnicode(wParam, lParam >> 16, state, unicode, UNICODE_BUFFER_LENGTH, 0);
487                         if(vkey == K_ALT || vkey == K_CTRL || vkey == K_SHIFT || charlength == 0)
488                                 unicode[0] = 0;
489                         else if(charlength == 2)
490                                 unicode[0] = unicode[1];
491                         if (!VID_JoyBlockEmulatedKeys(vkey))
492                                 Key_Event(vkey, unicode[0], down);
493                         break;
494
495                 case WM_SYSCHAR:
496                 // keep Alt-Space from happening
497                         break;
498
499                 case WM_SYSCOMMAND:
500                         // prevent screensaver from occuring while the active window
501                         // note: password-locked screensavers on Vista still work
502                         if (vid_activewindow && ((wParam & 0xFFF0) == SC_SCREENSAVE || (wParam & 0xFFF0) == SC_MONITORPOWER))
503                                 lRet = 0;
504                         else
505                                 lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
506                         break;
507
508         // this is complicated because Win32 seems to pack multiple mouse events into
509         // one update sometimes, so we always check all states and look for events
510                 case WM_LBUTTONDOWN:
511                 case WM_LBUTTONUP:
512                 case WM_RBUTTONDOWN:
513                 case WM_RBUTTONUP:
514                 case WM_MBUTTONDOWN:
515                 case WM_MBUTTONUP:
516                 case WM_XBUTTONDOWN:   // backslash :: imouse explorer buttons
517                 case WM_XBUTTONUP:      // backslash :: imouse explorer buttons
518                 case WM_MOUSEMOVE:
519                         temp = 0;
520
521                         if (wParam & MK_LBUTTON)
522                                 temp |= 1;
523
524                         if (wParam & MK_RBUTTON)
525                                 temp |= 2;
526
527                         if (wParam & MK_MBUTTON)
528                                 temp |= 4;
529
530                         /* backslash :: imouse explorer buttons */
531                         if (wParam & MK_XBUTTON1)
532                                 temp |= 8;
533
534                         if (wParam & MK_XBUTTON2)
535                                 temp |= 16;
536                         /* :: backslash */
537
538                         // LordHavoc: lets hope this allows more buttons in the future...
539                         if (wParam & MK_XBUTTON3)
540                                 temp |= 32;
541                         if (wParam & MK_XBUTTON4)
542                                 temp |= 64;
543                         if (wParam & MK_XBUTTON5)
544                                 temp |= 128;
545                         if (wParam & MK_XBUTTON6)
546                                 temp |= 256;
547                         if (wParam & MK_XBUTTON7)
548                                 temp |= 512;
549
550 #ifdef SUPPORTDIRECTX
551                         if (!dinput_acquired)
552 #endif
553                         {
554                                 // perform button actions
555                                 int i;
556                                 for (i=0 ; i<mouse_buttons && i < 16 ; i++)
557                                         if ((temp ^ mouse_oldbuttonstate) & (1<<i))
558                                                 Key_Event (buttonremap[i], 0, (temp & (1<<i)) != 0);
559                                 mouse_oldbuttonstate = temp;
560                         }
561
562                         break;
563
564                 // JACK: This is the mouse wheel with the Intellimouse
565                 // Its delta is either positive or neg, and we generate the proper
566                 // Event.
567                 case WM_MOUSEWHEEL:
568                         if ((short) HIWORD(wParam) > 0) {
569                                 Key_Event(K_MWHEELUP, 0, true);
570                                 Key_Event(K_MWHEELUP, 0, false);
571                         } else {
572                                 Key_Event(K_MWHEELDOWN, 0, true);
573                                 Key_Event(K_MWHEELDOWN, 0, false);
574                         }
575                         break;
576
577                 case WM_SIZE:
578                         break;
579
580                 case WM_CLOSE:
581                         if (MessageBox (mainwindow, "Are you sure you want to quit?", "Confirm Exit", MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES)
582                                 Sys_Quit (0);
583
584                         break;
585
586                 case WM_ACTIVATE:
587                         fActive = LOWORD(wParam);
588                         fMinimized = (BOOL) HIWORD(wParam);
589                         AppActivate(!(fActive == WA_INACTIVE), fMinimized);
590
591                 // fix the leftover Alt from any Alt-Tab or the like that switched us away
592                         ClearAllStates ();
593
594                         break;
595
596                 //case WM_DESTROY:
597                 //      PostQuitMessage (0);
598                 //      break;
599
600                 case MM_MCINOTIFY:
601 #ifdef CONFIG_CD
602                         lRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam);
603 #endif
604                         break;
605
606                 default:
607                         /* pass all unhandled messages to DefWindowProc */
608                         lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
609                 break;
610         }
611
612         /* return 1 if handled message, 0 if not */
613         return lRet;
614 }
615
616 static void GL_CloseLibrary(void)
617 {
618         if (gldll)
619         {
620                 FreeLibrary(gldll);
621                 gldll = 0;
622                 gl_driver[0] = 0;
623                 qwglGetProcAddress = NULL;
624                 gl_extensions = "";
625                 gl_platform = "";
626                 gl_platformextensions = "";
627         }
628 }
629
630 static int GL_OpenLibrary(const char *name)
631 {
632         Con_Printf("Loading OpenGL driver %s\n", name);
633         GL_CloseLibrary();
634         if (!(gldll = LoadLibrary(name)))
635         {
636                 Con_Printf("Unable to LoadLibrary %s\n", name);
637                 return false;
638         }
639         strlcpy(gl_driver, name, sizeof(gl_driver));
640         return true;
641 }
642
643 void *GL_GetProcAddress(const char *name)
644 {
645         if (gldll)
646         {
647                 void *p = NULL;
648                 if (qwglGetProcAddress != NULL)
649                         p = (void *) qwglGetProcAddress(name);
650                 if (p == NULL)
651                         p = (void *) GetProcAddress(gldll, name);
652                 return p;
653         }
654         else
655                 return NULL;
656 }
657
658 #ifndef WGL_ARB_pixel_format
659 #define WGL_NUMBER_PIXEL_FORMATS_ARB   0x2000
660 #define WGL_DRAW_TO_WINDOW_ARB         0x2001
661 #define WGL_DRAW_TO_BITMAP_ARB         0x2002
662 #define WGL_ACCELERATION_ARB           0x2003
663 #define WGL_NEED_PALETTE_ARB           0x2004
664 #define WGL_NEED_SYSTEM_PALETTE_ARB    0x2005
665 #define WGL_SWAP_LAYER_BUFFERS_ARB     0x2006
666 #define WGL_SWAP_METHOD_ARB            0x2007
667 #define WGL_NUMBER_OVERLAYS_ARB        0x2008
668 #define WGL_NUMBER_UNDERLAYS_ARB       0x2009
669 #define WGL_TRANSPARENT_ARB            0x200A
670 #define WGL_TRANSPARENT_RED_VALUE_ARB  0x2037
671 #define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038
672 #define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039
673 #define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A
674 #define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B
675 #define WGL_SHARE_DEPTH_ARB            0x200C
676 #define WGL_SHARE_STENCIL_ARB          0x200D
677 #define WGL_SHARE_ACCUM_ARB            0x200E
678 #define WGL_SUPPORT_GDI_ARB            0x200F
679 #define WGL_SUPPORT_OPENGL_ARB         0x2010
680 #define WGL_DOUBLE_BUFFER_ARB          0x2011
681 #define WGL_STEREO_ARB                 0x2012
682 #define WGL_PIXEL_TYPE_ARB             0x2013
683 #define WGL_COLOR_BITS_ARB             0x2014
684 #define WGL_RED_BITS_ARB               0x2015
685 #define WGL_RED_SHIFT_ARB              0x2016
686 #define WGL_GREEN_BITS_ARB             0x2017
687 #define WGL_GREEN_SHIFT_ARB            0x2018
688 #define WGL_BLUE_BITS_ARB              0x2019
689 #define WGL_BLUE_SHIFT_ARB             0x201A
690 #define WGL_ALPHA_BITS_ARB             0x201B
691 #define WGL_ALPHA_SHIFT_ARB            0x201C
692 #define WGL_ACCUM_BITS_ARB             0x201D
693 #define WGL_ACCUM_RED_BITS_ARB         0x201E
694 #define WGL_ACCUM_GREEN_BITS_ARB       0x201F
695 #define WGL_ACCUM_BLUE_BITS_ARB        0x2020
696 #define WGL_ACCUM_ALPHA_BITS_ARB       0x2021
697 #define WGL_DEPTH_BITS_ARB             0x2022
698 #define WGL_STENCIL_BITS_ARB           0x2023
699 #define WGL_AUX_BUFFERS_ARB            0x2024
700 #define WGL_NO_ACCELERATION_ARB        0x2025
701 #define WGL_GENERIC_ACCELERATION_ARB   0x2026
702 #define WGL_FULL_ACCELERATION_ARB      0x2027
703 #define WGL_SWAP_EXCHANGE_ARB          0x2028
704 #define WGL_SWAP_COPY_ARB              0x2029
705 #define WGL_SWAP_UNDEFINED_ARB         0x202A
706 #define WGL_TYPE_RGBA_ARB              0x202B
707 #define WGL_TYPE_COLORINDEX_ARB        0x202C
708 #endif
709
710 #ifndef WGL_ARB_multisample
711 #define WGL_SAMPLE_BUFFERS_ARB         0x2041
712 #define WGL_SAMPLES_ARB                0x2042
713 #endif
714
715
716 static void IN_Init(void);
717 void VID_Init(void)
718 {
719         WNDCLASS wc;
720
721         InitCommonControls();
722         hIcon = LoadIcon (global_hInstance, MAKEINTRESOURCE (IDI_ICON1));
723
724         // Register the frame class
725         wc.style         = 0;
726         wc.lpfnWndProc   = (WNDPROC)MainWndProc;
727         wc.cbClsExtra    = 0;
728         wc.cbWndExtra    = 0;
729         wc.hInstance     = global_hInstance;
730         wc.hIcon         = hIcon;
731         wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
732         wc.hbrBackground = NULL;
733         wc.lpszMenuName  = 0;
734         wc.lpszClassName = "DarkPlacesWindowClass";
735
736         if (!RegisterClass (&wc))
737                 Con_Printf ("Couldn't register window class\n");
738
739         memset(&initialdevmode, 0, sizeof(initialdevmode));
740         EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &initialdevmode);
741
742         desktop_mode.width = initialdevmode.dmPelsWidth;
743         desktop_mode.height = initialdevmode.dmPelsHeight;
744         desktop_mode.bpp = initialdevmode.dmBitsPerPel;
745         desktop_mode.refreshrate = initialdevmode.dmDisplayFrequency;
746         desktop_mode.pixelheight_num = 1;
747         desktop_mode.pixelheight_denom = 1; // Win32 apparently does not provide this (FIXME)
748
749         IN_Init();
750 }
751
752 qboolean VID_InitModeGL(viddef_mode_t *mode)
753 {
754         int i;
755         HDC hdc;
756         RECT rect;
757         MSG msg;
758         PIXELFORMATDESCRIPTOR pfd =
759         {
760                 sizeof(PIXELFORMATDESCRIPTOR),  // size of this pfd
761                 1,                              // version number
762                 PFD_DRAW_TO_WINDOW              // support window
763                 |  PFD_SUPPORT_OPENGL   // support OpenGL
764                 |  PFD_DOUBLEBUFFER ,   // double buffered
765                 PFD_TYPE_RGBA,                  // RGBA type
766                 24,                             // 24-bit color depth
767                 0, 0, 0, 0, 0, 0,               // color bits ignored
768                 0,                              // no alpha buffer
769                 0,                              // shift bit ignored
770                 0,                              // no accumulation buffer
771                 0, 0, 0, 0,                     // accum bits ignored
772                 32,                             // 32-bit z-buffer
773                 0,                              // no stencil buffer
774                 0,                              // no auxiliary buffer
775                 PFD_MAIN_PLANE,                 // main layer
776                 0,                              // reserved
777                 0, 0, 0                         // layer masks ignored
778         };
779         int windowpass;
780         int pixelformat, newpixelformat;
781         UINT numpixelformats;
782         DWORD WindowStyle, ExWindowStyle;
783         const char *gldrivername;
784         int depth;
785         DEVMODE thismode;
786         qboolean foundmode, foundgoodmode;
787         int *a;
788         float *af;
789         int attribs[128];
790         float attribsf[16];
791         int bpp = mode->bitsperpixel;
792         int width = mode->width;
793         int height = mode->height;
794         int refreshrate = (int)floor(mode->refreshrate+0.5);
795         int stereobuffer = mode->stereobuffer;
796         int samples = mode->samples;
797         int fullscreen = mode->fullscreen;
798
799         if (vid_initialized)
800                 Sys_Error("VID_InitMode called when video is already initialised");
801
802         // if stencil is enabled, ask for alpha too
803         if (bpp >= 32)
804         {
805                 pfd.cRedBits = 8;
806                 pfd.cGreenBits = 8;
807                 pfd.cBlueBits = 8;
808                 pfd.cAlphaBits = 8;
809                 pfd.cDepthBits = 24;
810                 pfd.cStencilBits = 8;
811         }
812         else
813         {
814                 pfd.cRedBits = 5;
815                 pfd.cGreenBits = 5;
816                 pfd.cBlueBits = 5;
817                 pfd.cAlphaBits = 0;
818                 pfd.cDepthBits = 16;
819                 pfd.cStencilBits = 0;
820         }
821
822         if (stereobuffer)
823                 pfd.dwFlags |= PFD_STEREO;
824
825         a = attribs;
826         af = attribsf;
827         *a++ = WGL_DRAW_TO_WINDOW_ARB;
828         *a++ = GL_TRUE;
829         *a++ = WGL_ACCELERATION_ARB;
830         *a++ = WGL_FULL_ACCELERATION_ARB;
831         *a++ = WGL_DOUBLE_BUFFER_ARB;
832         *a++ = true;
833
834         if (bpp >= 32)
835         {
836                 *a++ = WGL_RED_BITS_ARB;
837                 *a++ = 8;
838                 *a++ = WGL_GREEN_BITS_ARB;
839                 *a++ = 8;
840                 *a++ = WGL_BLUE_BITS_ARB;
841                 *a++ = 8;
842                 *a++ = WGL_ALPHA_BITS_ARB;
843                 *a++ = 8;
844                 *a++ = WGL_DEPTH_BITS_ARB;
845                 *a++ = 24;
846                 *a++ = WGL_STENCIL_BITS_ARB;
847                 *a++ = 8;
848         }
849         else
850         {
851                 *a++ = WGL_RED_BITS_ARB;
852                 *a++ = 1;
853                 *a++ = WGL_GREEN_BITS_ARB;
854                 *a++ = 1;
855                 *a++ = WGL_BLUE_BITS_ARB;
856                 *a++ = 1;
857                 *a++ = WGL_DEPTH_BITS_ARB;
858                 *a++ = 16;
859         }
860
861         if (stereobuffer)
862         {
863                 *a++ = WGL_STEREO_ARB;
864                 *a++ = GL_TRUE;
865         }
866
867         if (samples > 1)
868         {
869                 *a++ = WGL_SAMPLE_BUFFERS_ARB;
870                 *a++ = 1;
871                 *a++ = WGL_SAMPLES_ARB;
872                 *a++ = samples;
873         }
874
875         *a = 0;
876         *af = 0;
877
878         gldrivername = "opengl32.dll";
879 // 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
880         i = COM_CheckParm("-gl_driver");
881         if (i && i < com_argc - 1)
882                 gldrivername = com_argv[i + 1];
883         if (!GL_OpenLibrary(gldrivername))
884         {
885                 Con_Printf("Unable to load GL driver %s\n", gldrivername);
886                 return false;
887         }
888
889         memset(&gdevmode, 0, sizeof(gdevmode));
890
891         vid_isfullscreen = false;
892         if (fullscreen)
893         {
894                 if(vid_desktopfullscreen.integer)
895                 {
896                         foundmode = true;
897                         gdevmode = initialdevmode;
898                         width = mode->width = gdevmode.dmPelsWidth;
899                         height = mode->height = gdevmode.dmPelsHeight;
900                         bpp = mode->bitsperpixel = gdevmode.dmBitsPerPel;
901                 }
902                 else if(vid_forcerefreshrate.integer)
903                 {
904                         foundmode = true;
905                         gdevmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
906                         gdevmode.dmBitsPerPel = bpp;
907                         gdevmode.dmPelsWidth = width;
908                         gdevmode.dmPelsHeight = height;
909                         gdevmode.dmSize = sizeof (gdevmode);
910                         if(refreshrate)
911                         {
912                                 gdevmode.dmFields |= DM_DISPLAYFREQUENCY;
913                                 gdevmode.dmDisplayFrequency = refreshrate;
914                         }
915                 }
916                 else
917                 {
918                         if(refreshrate == 0)
919                                 refreshrate = initialdevmode.dmDisplayFrequency; // default vid_refreshrate to the rate of the desktop
920
921                         foundmode = false;
922                         foundgoodmode = false;
923
924                         thismode.dmSize = sizeof(thismode);
925                         thismode.dmDriverExtra = 0;
926                         for(i = 0; EnumDisplaySettings(NULL, i, &thismode); ++i)
927                         {
928                                 if(~thismode.dmFields & (DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY))
929                                 {
930                                         Con_DPrintf("enumerating modes yielded a bogus item... please debug this\n");
931                                         continue;
932                                 }
933                                 if(developer_extra.integer)
934                                         Con_DPrintf("Found mode %dx%dx%dbpp %dHz... ", (int)thismode.dmPelsWidth, (int)thismode.dmPelsHeight, (int)thismode.dmBitsPerPel, (int)thismode.dmDisplayFrequency);
935                                 if(thismode.dmBitsPerPel != (DWORD)bpp)
936                                 {
937                                         if(developer_extra.integer)
938                                                 Con_DPrintf("wrong bpp\n");
939                                         continue;
940                                 }
941                                 if(thismode.dmPelsWidth != (DWORD)width)
942                                 {
943                                         if(developer_extra.integer)
944                                                 Con_DPrintf("wrong width\n");
945                                         continue;
946                                 }
947                                 if(thismode.dmPelsHeight != (DWORD)height)
948                                 {
949                                         if(developer_extra.integer)
950                                                 Con_DPrintf("wrong height\n");
951                                         continue;
952                                 }
953
954                                 if(foundgoodmode)
955                                 {
956                                         // if we have a good mode, make sure this mode is better than the previous one, and allowed by the refreshrate
957                                         if(thismode.dmDisplayFrequency > (DWORD)refreshrate)
958                                         {
959                                                 if(developer_extra.integer)
960                                                         Con_DPrintf("too high refresh rate\n");
961                                                 continue;
962                                         }
963                                         else if(thismode.dmDisplayFrequency <= gdevmode.dmDisplayFrequency)
964                                         {
965                                                 if(developer_extra.integer)
966                                                         Con_DPrintf("doesn't beat previous best match (too low)\n");
967                                                 continue;
968                                         }
969                                 }
970                                 else if(foundmode)
971                                 {
972                                         // we do have one, but it isn't good... make sure it has a lower frequency than the previous one
973                                         if(thismode.dmDisplayFrequency >= gdevmode.dmDisplayFrequency)
974                                         {
975                                                 if(developer_extra.integer)
976                                                         Con_DPrintf("doesn't beat previous best match (too high)\n");
977                                                 continue;
978                                         }
979                                 }
980                                 // otherwise, take anything
981
982                                 memcpy(&gdevmode, &thismode, sizeof(gdevmode));
983                                 if(thismode.dmDisplayFrequency <= (DWORD)refreshrate)
984                                         foundgoodmode = true;
985                                 else
986                                 {
987                                         if(developer_extra.integer)
988                                                 Con_DPrintf("(out of range)\n");
989                                 }
990                                 foundmode = true;
991                                 if(developer_extra.integer)
992                                         Con_DPrintf("accepted\n");
993                         }
994                 }
995
996                 if (!foundmode)
997                 {
998                         VID_Shutdown();
999                         Con_Printf("Unable to find the requested mode %dx%dx%dbpp\n", width, height, bpp);
1000                         return false;
1001                 }
1002                 else if(ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
1003                 {
1004                         VID_Shutdown();
1005                         Con_Printf("Unable to change to requested mode %dx%dx%dbpp\n", width, height, bpp);
1006                         return false;
1007                 }
1008
1009                 vid_isfullscreen = true;
1010                 WindowStyle = WS_POPUP;
1011                 ExWindowStyle = WS_EX_TOPMOST;
1012         }
1013         else
1014         {
1015                 hdc = GetDC (NULL);
1016                 i = GetDeviceCaps(hdc, RASTERCAPS);
1017                 depth = GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL);
1018                 ReleaseDC (NULL, hdc);
1019                 if (i & RC_PALETTE)
1020                 {
1021                         VID_Shutdown();
1022                         Con_Print("Can't run in non-RGB mode\n");
1023                         return false;
1024                 }
1025                 if (bpp > depth)
1026                 {
1027                         VID_Shutdown();
1028                         Con_Print("A higher desktop depth is required to run this video mode\n");
1029                         return false;
1030                 }
1031
1032                 WindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
1033                 ExWindowStyle = 0;
1034         }
1035
1036         AdjustWindowBounds(fullscreen, &width, &height, mode, WindowStyle, &rect);
1037
1038         pixelformat = 0;
1039         newpixelformat = 0;
1040         // start out at the final windowpass if samples is 1 as it's the only feature we need extended pixel formats for
1041         for (windowpass = samples == 1;windowpass < 2;windowpass++)
1042         {
1043                 gl_extensions = "";
1044                 gl_platformextensions = "";
1045
1046                 mainwindow = CreateWindowEx (ExWindowStyle, "DarkPlacesWindowClass", gamename, WindowStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, global_hInstance, NULL);
1047                 if (!mainwindow)
1048                 {
1049                         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);
1050                         VID_Shutdown();
1051                         return false;
1052                 }
1053
1054                 baseDC = GetDC(mainwindow);
1055
1056                 if (!newpixelformat)
1057                         newpixelformat = ChoosePixelFormat(baseDC, &pfd);
1058                 pixelformat = newpixelformat;
1059                 if (!pixelformat)
1060                 {
1061                         VID_Shutdown();
1062                         Con_Printf("ChoosePixelFormat(%p, %p) failed\n", (void *)baseDC, (void *)&pfd);
1063                         return false;
1064                 }
1065
1066                 if (SetPixelFormat(baseDC, pixelformat, &pfd) == false)
1067                 {
1068                         VID_Shutdown();
1069                         Con_Printf("SetPixelFormat(%p, %d, %p) failed\n", (void *)baseDC, pixelformat, (void *)&pfd);
1070                         return false;
1071                 }
1072
1073                 if (!GL_CheckExtension("wgl", wglfuncs, NULL, false))
1074                 {
1075                         VID_Shutdown();
1076                         Con_Print("wgl functions not found\n");
1077                         return false;
1078                 }
1079
1080                 baseRC = qwglCreateContext(baseDC);
1081                 if (!baseRC)
1082                 {
1083                         VID_Shutdown();
1084                         Con_Print("Could not initialize GL (wglCreateContext failed).\n\nMake sure you are in 65536 color mode, and try running -window.\n");
1085                         return false;
1086                 }
1087                 if (!qwglMakeCurrent(baseDC, baseRC))
1088                 {
1089                         VID_Shutdown();
1090                         Con_Printf("wglMakeCurrent(%p, %p) failed\n", (void *)baseDC, (void *)baseRC);
1091                         return false;
1092                 }
1093
1094                 if ((qglGetString = (const GLubyte* (GLAPIENTRY *)(GLenum name))GL_GetProcAddress("glGetString")) == NULL)
1095                 {
1096                         VID_Shutdown();
1097                         Con_Print("glGetString not found\n");
1098                         return false;
1099                 }
1100                 if ((qwglGetExtensionsStringARB = (const char *(WINAPI *)(HDC hdc))GL_GetProcAddress("wglGetExtensionsStringARB")) == NULL)
1101                         Con_Print("wglGetExtensionsStringARB not found\n");
1102
1103                 gl_extensions = (const char *)qglGetString(GL_EXTENSIONS);
1104                 gl_platform = "WGL";
1105                 gl_platformextensions = "";
1106
1107                 if (qwglGetExtensionsStringARB)
1108                         gl_platformextensions = (const char *)qwglGetExtensionsStringARB(baseDC);
1109
1110                 if (!gl_extensions)
1111                         gl_extensions = "";
1112                 if (!gl_platformextensions)
1113                         gl_platformextensions = "";
1114
1115                 // now some nice Windows pain:
1116                 // we have created a window, we needed one to find out if there are
1117                 // any multisample pixel formats available, the problem is that to
1118                 // actually use one of those multisample formats we now have to
1119                 // recreate the window (yes Microsoft OpenGL really is that bad)
1120
1121                 if (windowpass == 0)
1122                 {
1123                         if (!GL_CheckExtension("WGL_ARB_pixel_format", wglpixelformatfuncs, "-noarbpixelformat", false) || !qwglChoosePixelFormatARB(baseDC, attribs, attribsf, 1, &newpixelformat, &numpixelformats) || !newpixelformat)
1124                                 break;
1125                         // ok we got one - do it all over again with newpixelformat
1126                         qwglMakeCurrent(NULL, NULL);
1127                         qwglDeleteContext(baseRC);baseRC = 0;
1128                         ReleaseDC(mainwindow, baseDC);baseDC = 0;
1129                         // eat up any messages waiting for us
1130                         while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
1131                         {
1132                                 TranslateMessage (&msg);
1133                                 DispatchMessage (&msg);
1134                         }
1135                 }
1136         }
1137
1138         /*
1139         if (!fullscreen)
1140                 SetWindowPos (mainwindow, NULL, CenterX, CenterY, 0, 0,SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME);
1141         */
1142
1143         ShowWindow (mainwindow, SW_SHOWDEFAULT);
1144         UpdateWindow (mainwindow);
1145
1146         // now we try to make sure we get the focus on the mode switch, because
1147         // sometimes in some systems we don't.  We grab the foreground, then
1148         // finish setting up, pump all our messages, and sleep for a little while
1149         // to let messages finish bouncing around the system, then we put
1150         // ourselves at the top of the z order, then grab the foreground again,
1151         // Who knows if it helps, but it probably doesn't hurt
1152         SetForegroundWindow (mainwindow);
1153
1154         while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
1155         {
1156                 TranslateMessage (&msg);
1157                 DispatchMessage (&msg);
1158         }
1159
1160         Sleep (100);
1161
1162         SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOCOPYBITS);
1163
1164         SetForegroundWindow (mainwindow);
1165
1166         // fix the leftover Alt from any Alt-Tab or the like that switched us away
1167         ClearAllStates ();
1168
1169 // COMMANDLINEOPTION: Windows WGL: -novideosync disables WGL_EXT_swap_control
1170         GL_CheckExtension("WGL_EXT_swap_control", wglswapintervalfuncs, "-novideosync", false);
1171
1172         GL_Init ();
1173
1174         //vid_menudrawfn = VID_MenuDraw;
1175         //vid_menukeyfn = VID_MenuKey;
1176         vid_usingmouse = false;
1177         vid_usinghidecursor = false;
1178         vid_usingvsync = false;
1179         vid_reallyhidden = vid_hidden = false;
1180         vid_initialized = true;
1181
1182         IN_StartupMouse ();
1183
1184         if (qwglSwapIntervalEXT)
1185         {
1186                 vid_usevsync = vid_vsync.integer != 0;
1187                 vid_usingvsync = vid_vsync.integer != 0;
1188                 qwglSwapIntervalEXT (vid_usevsync);
1189         }
1190
1191         return true;
1192 }
1193
1194 static void AdjustWindowBounds(int fullscreen, int *width, int *height, viddef_mode_t *mode, DWORD WindowStyle, RECT *rect)
1195 {
1196         int CenterX, CenterY;
1197
1198         rect->top = 0;
1199         rect->left = 0;
1200         rect->right = *width;
1201         rect->bottom = *height;
1202         AdjustWindowRectEx(rect, WindowStyle, false, 0);
1203
1204         if (fullscreen)
1205         {
1206                 CenterX = 0;
1207                 CenterY = 0;
1208         }
1209         else
1210         {
1211                 RECT workArea;
1212                 SystemParametersInfo(SPI_GETWORKAREA, NULL, &workArea, 0);
1213                 int workWidth = workArea.right - workArea.left;
1214                 int workHeight = workArea.bottom - workArea.top;
1215
1216                 // if height/width matches physical screen height/width, adjust it to available desktop size
1217                 // and allow 2 pixels on top for the title bar so the window can be moved
1218                 const int titleBarPixels = 2;
1219                 if (*width == GetSystemMetrics(SM_CXSCREEN) && (*height == GetSystemMetrics(SM_CYSCREEN) || *height == workHeight - titleBarPixels))
1220                 {
1221                         rect->right -= *width - workWidth;
1222                         *width = mode->width = workWidth;
1223                         rect->bottom -= *height - (workHeight - titleBarPixels);
1224                         *height = mode->height = workHeight - titleBarPixels;
1225                         CenterX = 0;
1226                         CenterY = titleBarPixels;
1227                 }
1228                 else
1229                 {
1230                         CenterX = max(0, (workWidth - *width) / 2);
1231                         CenterY = max(0, (workHeight - *height) / 2);
1232                 }
1233         }
1234
1235         // x and y may be changed by WM_MOVE messages
1236         window_x = CenterX;
1237         window_y = CenterY;
1238         rect->left += CenterX;
1239         rect->right += CenterX;
1240         rect->top += CenterY;
1241         rect->bottom += CenterY;
1242 }
1243
1244 qboolean VID_InitMode(viddef_mode_t *mode)
1245 {
1246         return VID_InitModeGL(mode);
1247 }
1248
1249
1250 static void IN_Shutdown(void);
1251 void VID_Shutdown (void)
1252 {
1253         qboolean isgl;
1254         if(vid_initialized == false)
1255                 return;
1256
1257         VID_EnableJoystick(false);
1258         VID_SetMouse(false, false, false);
1259
1260         vid_initialized = false;
1261         isgl = gldll != NULL;
1262         IN_Shutdown();
1263         gl_driver[0] = 0;
1264         gl_extensions = "";
1265         gl_platform = "";
1266         gl_platformextensions = "";
1267         if (qwglMakeCurrent)
1268                 qwglMakeCurrent(NULL, NULL);
1269         qwglMakeCurrent = NULL;
1270         if (baseRC && qwglDeleteContext)
1271                 qwglDeleteContext(baseRC);
1272         qwglDeleteContext = NULL;
1273         // close the library before we get rid of the window
1274         GL_CloseLibrary();
1275         if (baseDC && mainwindow)
1276                 ReleaseDC(mainwindow, baseDC);
1277         baseDC = NULL;
1278         AppActivate(false, false);
1279         if (mainwindow)
1280                 DestroyWindow(mainwindow);
1281         mainwindow = 0;
1282         if (vid_isfullscreen && isgl)
1283                 ChangeDisplaySettings (NULL, CDS_FULLSCREEN);
1284         vid_isfullscreen = false;
1285 }
1286
1287 void VID_SetMouse(qboolean fullscreengrab, qboolean relative, qboolean hidecursor)
1288 {
1289         static qboolean restore_spi;
1290         static int originalmouseparms[3];
1291
1292         if (!mouseinitialized)
1293                 return;
1294
1295         if (relative)
1296         {
1297                 if (!vid_usingmouse)
1298                 {
1299                         vid_usingmouse = true;
1300                         cl_ignoremousemoves = 2;
1301 #ifdef SUPPORTDIRECTX
1302                         if (dinput && g_pMouse)
1303                         {
1304                                 IDirectInputDevice_Acquire(g_pMouse);
1305                                 dinput_acquired = true;
1306                         }
1307                         else
1308 #endif
1309                         {
1310                                 RECT window_rect;
1311                                 window_rect.left = window_x;
1312                                 window_rect.top = window_y;
1313                                 window_rect.right = window_x + vid.width;
1314                                 window_rect.bottom = window_y + vid.height;
1315
1316                                 // change mouse settings to turn off acceleration
1317 // COMMANDLINEOPTION: Windows GDI Input: -noforcemparms disables setting of mouse parameters (not used with -dinput, windows only)
1318                                 if (!COM_CheckParm ("-noforcemparms") && SystemParametersInfo (SPI_GETMOUSE, 0, originalmouseparms, 0))
1319                                 {
1320                                         int newmouseparms[3];
1321                                         newmouseparms[0] = 0; // threshold to double movement (only if accel level is >= 1)
1322                                         newmouseparms[1] = 0; // threshold to quadruple movement (only if accel level is >= 2)
1323                                         newmouseparms[2] = 0; // maximum level of acceleration (0 = off)
1324                                         restore_spi = SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0) != FALSE;
1325                                 }
1326                                 else
1327                                         restore_spi = false;
1328                                 SetCursorPos ((window_x + vid.width / 2), (window_y + vid.height / 2));
1329
1330                                 SetCapture (mainwindow);
1331                                 ClipCursor (&window_rect);
1332                         }
1333                 }
1334         }
1335         else
1336         {
1337                 if (vid_usingmouse)
1338                 {
1339                         vid_usingmouse = false;
1340                         cl_ignoremousemoves = 2;
1341 #ifdef SUPPORTDIRECTX
1342                         if (dinput_acquired)
1343                         {
1344                                 IDirectInputDevice_Unacquire(g_pMouse);
1345                                 dinput_acquired = false;
1346                         }
1347                         else
1348 #endif
1349                         {
1350                                 // restore system mouseparms if we changed them
1351                                 if (restore_spi)
1352                                         SystemParametersInfo (SPI_SETMOUSE, 0, originalmouseparms, 0);
1353                                 restore_spi = false;
1354                                 ClipCursor (NULL);
1355                                 ReleaseCapture ();
1356                         }
1357                 }
1358         }
1359
1360         if (vid_usinghidecursor != hidecursor)
1361         {
1362                 vid_usinghidecursor = hidecursor;
1363                 ShowCursor (!hidecursor);
1364         }
1365 }
1366
1367 void VID_BuildJoyState(vid_joystate_t *joystate)
1368 {
1369         VID_Shared_BuildJoyState_Begin(joystate);
1370         VID_Shared_BuildJoyState_Finish(joystate);
1371 }
1372
1373 void VID_EnableJoystick(qboolean enable)
1374 {
1375         int index = joy_enable.integer > 0 ? joy_index.integer : -1;
1376         qboolean success = false;
1377         int sharedcount = 0;
1378         sharedcount = VID_Shared_SetJoystick(index);
1379         if (index >= 0 && index < sharedcount)
1380                 success = true;
1381
1382         // update cvar containing count of XInput joysticks
1383         if (joy_detected.integer != sharedcount)
1384                 Cvar_SetValueQuick(&joy_detected, sharedcount);
1385
1386         if (joy_active.integer != (success ? 1 : 0))
1387                 Cvar_SetValueQuick(&joy_active, success ? 1 : 0);
1388 }
1389
1390 #ifdef SUPPORTDIRECTX
1391 /*
1392 ===========
1393 IN_InitDInput
1394 ===========
1395 */
1396 static qboolean IN_InitDInput (void)
1397 {
1398     HRESULT             hr;
1399         DIPROPDWORD     dipdw = {
1400                 {
1401                         sizeof(DIPROPDWORD),        // diph.dwSize
1402                         sizeof(DIPROPHEADER),       // diph.dwHeaderSize
1403                         0,                          // diph.dwObj
1404                         DIPH_DEVICE,                // diph.dwHow
1405                 },
1406                 DINPUT_BUFFERSIZE,              // dwData
1407         };
1408
1409         if (!hInstDI)
1410         {
1411                 hInstDI = LoadLibrary("dinput.dll");
1412
1413                 if (hInstDI == NULL)
1414                 {
1415                         Con_Print("Couldn't load dinput.dll\n");
1416                         return false;
1417                 }
1418         }
1419
1420         if (!pDirectInputCreate)
1421         {
1422                 pDirectInputCreate = (HRESULT (__stdcall *)(HINSTANCE,DWORD,LPDIRECTINPUT *,LPUNKNOWN))GetProcAddress(hInstDI,"DirectInputCreateA");
1423
1424                 if (!pDirectInputCreate)
1425                 {
1426                         Con_Print("Couldn't get DI proc addr\n");
1427                         return false;
1428                 }
1429         }
1430
1431 // register with DirectInput and get an IDirectInput to play with.
1432         hr = iDirectInputCreate(global_hInstance, DIRECTINPUT_VERSION, &g_pdi, NULL);
1433
1434         if (FAILED(hr))
1435         {
1436                 return false;
1437         }
1438
1439 // obtain an interface to the system mouse device.
1440 #ifdef __cplusplus
1441         hr = IDirectInput_CreateDevice(g_pdi, GUID_SysMouse, &g_pMouse, NULL);
1442 #else
1443         hr = IDirectInput_CreateDevice(g_pdi, &GUID_SysMouse, &g_pMouse, NULL);
1444 #endif
1445
1446         if (FAILED(hr))
1447         {
1448                 Con_Print("Couldn't open DI mouse device\n");
1449                 return false;
1450         }
1451
1452 // set the data format to "mouse format".
1453         hr = IDirectInputDevice_SetDataFormat(g_pMouse, &c_dfDIMouse);
1454
1455         if (FAILED(hr))
1456         {
1457                 Con_Print("Couldn't set DI mouse format\n");
1458                 return false;
1459         }
1460
1461 // set the cooperativity level.
1462         hr = IDirectInputDevice_SetCooperativeLevel(g_pMouse, mainwindow,
1463                         DISCL_EXCLUSIVE | DISCL_FOREGROUND);
1464
1465         if (FAILED(hr))
1466         {
1467                 Con_Print("Couldn't set DI coop level\n");
1468                 return false;
1469         }
1470
1471
1472 // set the buffer size to DINPUT_BUFFERSIZE elements.
1473 // the buffer size is a DWORD property associated with the device
1474         hr = IDirectInputDevice_SetProperty(g_pMouse, DIPROP_BUFFERSIZE, &dipdw.diph);
1475
1476         if (FAILED(hr))
1477         {
1478                 Con_Print("Couldn't set DI buffersize\n");
1479                 return false;
1480         }
1481
1482         return true;
1483 }
1484 #endif
1485
1486
1487 /*
1488 ===========
1489 IN_StartupMouse
1490 ===========
1491 */
1492 static void IN_StartupMouse (void)
1493 {
1494         if (COM_CheckParm ("-nomouse"))
1495                 return;
1496
1497         mouseinitialized = true;
1498
1499 #ifdef SUPPORTDIRECTX
1500 // COMMANDLINEOPTION: Windows Input: -dinput enables DirectInput for mouse input
1501         if (COM_CheckParm ("-dinput"))
1502                 dinput = IN_InitDInput ();
1503
1504         if (dinput)
1505                 Con_Print("DirectInput initialized\n");
1506         else
1507                 Con_Print("DirectInput not initialized\n");
1508 #endif
1509
1510         mouse_buttons = 10;
1511 }
1512
1513
1514 /*
1515 ===========
1516 IN_MouseMove
1517 ===========
1518 */
1519 static void IN_MouseMove (void)
1520 {
1521         POINT current_pos;
1522
1523         GetCursorPos (&current_pos);
1524         in_windowmouse_x = current_pos.x - window_x;
1525         in_windowmouse_y = current_pos.y - window_y;
1526
1527         if (!vid_usingmouse)
1528                 return;
1529
1530 #ifdef SUPPORTDIRECTX
1531         if (dinput_acquired)
1532         {
1533                 int i;
1534                 DIDEVICEOBJECTDATA      od;
1535                 DWORD                           dwElements;
1536                 HRESULT                         hr;
1537
1538                 for (;;)
1539                 {
1540                         dwElements = 1;
1541
1542                         hr = IDirectInputDevice_GetDeviceData(g_pMouse,
1543                                         sizeof(DIDEVICEOBJECTDATA), &od, &dwElements, 0);
1544
1545                         if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED))
1546                         {
1547                                 IDirectInputDevice_Acquire(g_pMouse);
1548                                 break;
1549                         }
1550
1551                         /* Unable to read data or no data available */
1552                         if (FAILED(hr) || dwElements == 0)
1553                                 break;
1554
1555                         /* Look at the element to see what happened */
1556
1557                         if ((int)od.dwOfs == DIMOFS_X)
1558                                 in_mouse_x += (LONG) od.dwData;
1559                         if ((int)od.dwOfs == DIMOFS_Y)
1560                                 in_mouse_y += (LONG) od.dwData;
1561                         if ((int)od.dwOfs == DIMOFS_Z)
1562                         {
1563                                 if((LONG)od.dwData < 0)
1564                                 {
1565                                         Key_Event(K_MWHEELDOWN, 0, true);
1566                                         Key_Event(K_MWHEELDOWN, 0, false);
1567                                 }
1568                                 else if((LONG)od.dwData > 0)
1569                                 {
1570                                         Key_Event(K_MWHEELUP, 0, true);
1571                                         Key_Event(K_MWHEELUP, 0, false);
1572                                 }
1573                         }
1574                         if ((int)od.dwOfs == DIMOFS_BUTTON0)
1575                                 mstate_di = (mstate_di & ~1) | ((od.dwData & 0x80) >> 7);
1576                         if ((int)od.dwOfs == DIMOFS_BUTTON1)
1577                                 mstate_di = (mstate_di & ~2) | ((od.dwData & 0x80) >> 6);
1578                         if ((int)od.dwOfs == DIMOFS_BUTTON2)
1579                                 mstate_di = (mstate_di & ~4) | ((od.dwData & 0x80) >> 5);
1580                         if ((int)od.dwOfs == DIMOFS_BUTTON3)
1581                                 mstate_di = (mstate_di & ~8) | ((od.dwData & 0x80) >> 4);
1582                 }
1583
1584                 // perform button actions
1585                 for (i=0 ; i<mouse_buttons && i < 16 ; i++)
1586                         if ((mstate_di ^ mouse_oldbuttonstate) & (1<<i))
1587                                 Key_Event (buttonremap[i], 0, (mstate_di & (1<<i)) != 0);
1588                 mouse_oldbuttonstate = mstate_di;
1589         }
1590         else
1591 #endif
1592         {
1593                 in_mouse_x += in_windowmouse_x - (int)(vid.width / 2);
1594                 in_mouse_y += in_windowmouse_y - (int)(vid.height / 2);
1595
1596                 // if the mouse has moved, force it to the center, so there's room to move
1597                 if (in_mouse_x || in_mouse_y)
1598                         SetCursorPos ((window_x + vid.width / 2), (window_y + vid.height / 2));
1599         }
1600 }
1601
1602
1603 /*
1604 ===========
1605 IN_Move
1606 ===========
1607 */
1608 void IN_Move (void)
1609 {
1610         vid_joystate_t joystate;
1611         if (vid_activewindow && !vid_reallyhidden)
1612                 IN_MouseMove ();
1613         VID_EnableJoystick(true);
1614         VID_BuildJoyState(&joystate);
1615         VID_ApplyJoyState(&joystate);
1616 }
1617
1618
1619 static void IN_Init(void)
1620 {
1621         uiWheelMessage = RegisterWindowMessage ( "MSWHEEL_ROLLMSG" );
1622         Cvar_RegisterVariable (&vid_forcerefreshrate);
1623 }
1624
1625 static void IN_Shutdown(void)
1626 {
1627 #ifdef SUPPORTDIRECTX
1628         if (g_pMouse)
1629                 IDirectInputDevice_Release(g_pMouse);
1630         g_pMouse = NULL;
1631
1632         if (g_pdi)
1633                 IDirectInput_Release(g_pdi);
1634         g_pdi = NULL;
1635 #endif
1636 }
1637
1638 vid_mode_t *VID_GetDesktopMode(void)
1639 {
1640         return &desktop_mode;
1641 }
1642
1643 size_t VID_ListModes(vid_mode_t *modes, size_t maxcount)
1644 {
1645         int i;
1646         size_t k;
1647         DEVMODE thismode;
1648
1649         thismode.dmSize = sizeof(thismode);
1650         thismode.dmDriverExtra = 0;
1651         k = 0;
1652         for(i = 0; EnumDisplaySettings(NULL, i, &thismode); ++i)
1653         {
1654                 if(~thismode.dmFields & (DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY))
1655                 {
1656                         Con_DPrintf("enumerating modes yielded a bogus item... please debug this\n");
1657                         continue;
1658                 }
1659                 if(k >= maxcount)
1660                         break;
1661                 modes[k].width = thismode.dmPelsWidth;
1662                 modes[k].height = thismode.dmPelsHeight;
1663                 modes[k].bpp = thismode.dmBitsPerPel;
1664                 modes[k].refreshrate = thismode.dmDisplayFrequency;
1665                 modes[k].pixelheight_num = 1;
1666                 modes[k].pixelheight_denom = 1; // Win32 apparently does not provide this (FIXME)
1667                 ++k;
1668         }
1669         return k;
1670 }