]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/sys/win32/win_glimp.cpp
Various Mac OS X tweaks to get this to build. Probably breaking things.
[icculus/iodoom3.git] / neo / sys / win32 / win_glimp.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28 /*
29 ** WIN_GLIMP.C
30 **
31 ** This file contains ALL Win32 specific stuff having to do with the
32 ** OpenGL refresh.  When a port is being made the following functions
33 ** must be implemented by the port:
34 **
35 ** GLimp_SwapBuffers
36 ** GLimp_Init
37 ** GLimp_Shutdown
38 ** GLimp_SetGamma
39 **
40 ** Note that the GLW_xxx functions are Windows specific GL-subsystem
41 ** related functions that are relevant ONLY to win_glimp.c
42 */
43 #include "../../idlib/precompiled.h"
44 #pragma hdrstop
45
46 #include "win_local.h"
47 #include "rc/AFEditor_resource.h"
48 #include "rc/doom_resource.h"
49 #include "../../renderer/tr_local.h"
50
51 static void             GLW_InitExtensions( void );
52
53
54 // WGL_ARB_extensions_string
55 PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB;
56
57 // WGL_EXT_swap_interval
58 PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT;
59
60 // WGL_ARB_pixel_format
61 PFNWGLGETPIXELFORMATATTRIBIVARBPROC wglGetPixelFormatAttribivARB;
62 PFNWGLGETPIXELFORMATATTRIBFVARBPROC wglGetPixelFormatAttribfvARB;
63 PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB;
64
65 // WGL_ARB_pbuffer
66 PFNWGLCREATEPBUFFERARBPROC      wglCreatePbufferARB;
67 PFNWGLGETPBUFFERDCARBPROC       wglGetPbufferDCARB;
68 PFNWGLRELEASEPBUFFERDCARBPROC   wglReleasePbufferDCARB;
69 PFNWGLDESTROYPBUFFERARBPROC     wglDestroyPbufferARB;
70 PFNWGLQUERYPBUFFERARBPROC       wglQueryPbufferARB;
71
72 // WGL_ARB_render_texture 
73 PFNWGLBINDTEXIMAGEARBPROC               wglBindTexImageARB;
74 PFNWGLRELEASETEXIMAGEARBPROC    wglReleaseTexImageARB;
75 PFNWGLSETPBUFFERATTRIBARBPROC   wglSetPbufferAttribARB;
76
77
78
79 /* ARB_pixel_format */
80 #define WGL_NUMBER_PIXEL_FORMATS_ARB       0x2000
81 #define WGL_DRAW_TO_WINDOW_ARB             0x2001
82 #define WGL_DRAW_TO_BITMAP_ARB             0x2002
83 #define WGL_ACCELERATION_ARB               0x2003
84 #define WGL_NEED_PALETTE_ARB               0x2004
85 #define WGL_NEED_SYSTEM_PALETTE_ARB        0x2005
86 #define WGL_SWAP_LAYER_BUFFERS_ARB         0x2006
87 #define WGL_SWAP_METHOD_ARB                0x2007
88 #define WGL_NUMBER_OVERLAYS_ARB            0x2008
89 #define WGL_NUMBER_UNDERLAYS_ARB           0x2009
90 #define WGL_TRANSPARENT_ARB                0x200A
91 #define WGL_SHARE_DEPTH_ARB                0x200C
92 #define WGL_SHARE_STENCIL_ARB              0x200D
93 #define WGL_SHARE_ACCUM_ARB                0x200E
94 #define WGL_SUPPORT_GDI_ARB                0x200F
95 #define WGL_SUPPORT_OPENGL_ARB             0x2010
96 #define WGL_DOUBLE_BUFFER_ARB              0x2011
97 #define WGL_STEREO_ARB                     0x2012
98 #define WGL_PIXEL_TYPE_ARB                 0x2013
99 #define WGL_COLOR_BITS_ARB                 0x2014
100 #define WGL_RED_BITS_ARB                   0x2015
101 #define WGL_RED_SHIFT_ARB                  0x2016
102 #define WGL_GREEN_BITS_ARB                 0x2017
103 #define WGL_GREEN_SHIFT_ARB                0x2018
104 #define WGL_BLUE_BITS_ARB                  0x2019
105 #define WGL_BLUE_SHIFT_ARB                 0x201A
106 #define WGL_ALPHA_BITS_ARB                 0x201B
107 #define WGL_ALPHA_SHIFT_ARB                0x201C
108 #define WGL_ACCUM_BITS_ARB                 0x201D
109 #define WGL_ACCUM_RED_BITS_ARB             0x201E
110 #define WGL_ACCUM_GREEN_BITS_ARB           0x201F
111 #define WGL_ACCUM_BLUE_BITS_ARB            0x2020
112 #define WGL_ACCUM_ALPHA_BITS_ARB           0x2021
113 #define WGL_DEPTH_BITS_ARB                 0x2022
114 #define WGL_STENCIL_BITS_ARB               0x2023
115 #define WGL_AUX_BUFFERS_ARB                0x2024
116 #define WGL_NO_ACCELERATION_ARB            0x2025
117 #define WGL_GENERIC_ACCELERATION_ARB       0x2026
118 #define WGL_FULL_ACCELERATION_ARB          0x2027
119 #define WGL_SWAP_EXCHANGE_ARB              0x2028
120 #define WGL_SWAP_COPY_ARB                  0x2029
121 #define WGL_SWAP_UNDEFINED_ARB             0x202A
122 #define WGL_TYPE_RGBA_ARB                  0x202B
123 #define WGL_TYPE_COLORINDEX_ARB            0x202C
124 #define WGL_TRANSPARENT_RED_VALUE_ARB      0x2037
125 #define WGL_TRANSPARENT_GREEN_VALUE_ARB    0x2038
126 #define WGL_TRANSPARENT_BLUE_VALUE_ARB     0x2039
127 #define WGL_TRANSPARENT_ALPHA_VALUE_ARB    0x203A
128 #define WGL_TRANSPARENT_INDEX_VALUE_ARB    0x203B
129
130 /* ARB_multisample */
131 #define WGL_SAMPLE_BUFFERS_ARB             0x2041
132 #define WGL_SAMPLES_ARB                    0x2042
133
134
135
136 //
137 // function declaration
138 //
139 bool QGL_Init( const char *dllname );
140 void     QGL_Shutdown( void );
141
142
143
144 /*
145 ========================
146 GLimp_GetOldGammaRamp
147 ========================
148 */
149 static void GLimp_SaveGamma( void ) {
150         HDC                     hDC;
151         BOOL            success;
152
153         hDC = GetDC( GetDesktopWindow() );
154         success = GetDeviceGammaRamp( hDC, win32.oldHardwareGamma );
155         common->DPrintf( "...getting default gamma ramp: %s\n", success ? "success" : "failed" );
156         ReleaseDC( GetDesktopWindow(), hDC );
157 }
158
159 /*
160 ========================
161 GLimp_RestoreGamma
162 ========================
163 */
164 static void GLimp_RestoreGamma( void ) {
165         HDC hDC;
166         BOOL success;
167
168         // if we never read in a reasonable looking
169         // table, don't write it out
170         if ( win32.oldHardwareGamma[0][255] == 0 ) {
171                 return;
172         }
173
174         hDC = GetDC( GetDesktopWindow() );
175         success = SetDeviceGammaRamp( hDC, win32.oldHardwareGamma );
176         common->DPrintf ( "...restoring hardware gamma: %s\n", success ? "success" : "failed" );
177         ReleaseDC( GetDesktopWindow(), hDC );
178 }
179
180
181 /*
182 ========================
183 GLimp_SetGamma
184
185 The renderer calls this when the user adjusts r_gamma or r_brightness
186 ========================
187 */
188 void GLimp_SetGamma( unsigned short red[256], unsigned short green[256], unsigned short blue[256] ) {
189         unsigned short table[3][256];
190         int i;
191
192         if ( !win32.hDC ) {
193                 return;
194         }
195
196         for ( i = 0; i < 256; i++ ) {
197                 table[0][i] = red[i];
198                 table[1][i] = green[i];
199                 table[2][i] = blue[i];
200         }
201
202         if ( !SetDeviceGammaRamp( win32.hDC, table ) ) {
203                 common->Printf( "WARNING: SetDeviceGammaRamp failed.\n" );
204         }
205 }
206
207 /*
208 =============================================================================
209
210 WglExtension Grabbing
211
212 This is gross -- creating a window just to get a context to get the wgl extensions
213
214 =============================================================================
215 */
216
217 /*
218 ====================
219 FakeWndProc
220
221 Only used to get wglExtensions
222 ====================
223 */
224 LONG WINAPI FakeWndProc (
225     HWND    hWnd,
226     UINT    uMsg,
227     WPARAM  wParam,
228     LPARAM  lParam) {
229
230         if ( uMsg == WM_DESTROY ) {
231         PostQuitMessage(0);
232         }
233
234         if ( uMsg != WM_CREATE ) {
235             return DefWindowProc(hWnd, uMsg, wParam, lParam);
236         }
237
238         const static PIXELFORMATDESCRIPTOR pfd = {
239                 sizeof(PIXELFORMATDESCRIPTOR),
240                 1,
241                 PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
242                 PFD_TYPE_RGBA,
243                 24,
244                 0, 0, 0, 0, 0, 0,
245                 8, 0,
246                 0, 0, 0, 0,
247                 24, 8,
248                 0,
249                 PFD_MAIN_PLANE,
250                 0,
251                 0,
252                 0,
253                 0,
254         };
255         int             pixelFormat;
256         HDC hDC;
257         HGLRC hGLRC;
258
259     hDC = GetDC(hWnd);
260
261     // Set up OpenGL
262     pixelFormat = ChoosePixelFormat(hDC, &pfd);
263     SetPixelFormat(hDC, pixelFormat, &pfd);
264     hGLRC = qwglCreateContext(hDC);
265     qwglMakeCurrent(hDC, hGLRC);
266
267         // free things
268     wglMakeCurrent(NULL, NULL);
269     wglDeleteContext(hGLRC);
270     ReleaseDC(hWnd, hDC);
271
272     return DefWindowProc(hWnd, uMsg, wParam, lParam);
273 }
274
275
276 /*
277 ==================
278 GLW_GetWGLExtensionsWithFakeWindow
279 ==================
280 */
281 void GLW_CheckWGLExtensions( HDC hDC ) {
282         wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)
283                                                           GLimp_ExtensionPointer("wglGetExtensionsStringARB");
284         if ( wglGetExtensionsStringARB ) {
285                 glConfig.wgl_extensions_string = (const char *) wglGetExtensionsStringARB(hDC);
286         } else {
287                 glConfig.wgl_extensions_string = "";
288         }
289
290         // WGL_EXT_swap_control
291         wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) GLimp_ExtensionPointer( "wglSwapIntervalEXT" );
292         r_swapInterval.SetModified();   // force a set next frame
293
294         // WGL_ARB_pixel_format
295         wglGetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC)GLimp_ExtensionPointer("wglGetPixelFormatAttribivARB");
296         wglGetPixelFormatAttribfvARB = (PFNWGLGETPIXELFORMATATTRIBFVARBPROC)GLimp_ExtensionPointer("wglGetPixelFormatAttribfvARB");
297         wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)GLimp_ExtensionPointer("wglChoosePixelFormatARB");
298
299         // WGL_ARB_pbuffer
300         wglCreatePbufferARB = (PFNWGLCREATEPBUFFERARBPROC)GLimp_ExtensionPointer("wglCreatePbufferARB");
301         wglGetPbufferDCARB = (PFNWGLGETPBUFFERDCARBPROC)GLimp_ExtensionPointer("wglGetPbufferDCARB");
302         wglReleasePbufferDCARB = (PFNWGLRELEASEPBUFFERDCARBPROC)GLimp_ExtensionPointer("wglReleasePbufferDCARB");
303         wglDestroyPbufferARB = (PFNWGLDESTROYPBUFFERARBPROC)GLimp_ExtensionPointer("wglDestroyPbufferARB");
304         wglQueryPbufferARB = (PFNWGLQUERYPBUFFERARBPROC)GLimp_ExtensionPointer("wglQueryPbufferARB");
305
306         // WGL_ARB_render_texture 
307         wglBindTexImageARB = (PFNWGLBINDTEXIMAGEARBPROC)GLimp_ExtensionPointer("wglBindTexImageARB");
308         wglReleaseTexImageARB = (PFNWGLRELEASETEXIMAGEARBPROC)GLimp_ExtensionPointer("wglReleaseTexImageARB");
309         wglSetPbufferAttribARB = (PFNWGLSETPBUFFERATTRIBARBPROC)GLimp_ExtensionPointer("wglSetPbufferAttribARB");
310 }
311
312 /*
313 ==================
314 GLW_GetWGLExtensionsWithFakeWindow
315 ==================
316 */
317 static void GLW_GetWGLExtensionsWithFakeWindow( void ) {
318         HWND    hWnd;
319     MSG         msg;
320
321     // Create a window for the sole purpose of getting
322         // a valid context to get the wglextensions
323     hWnd = CreateWindow(WIN32_FAKE_WINDOW_CLASS_NAME, GAME_NAME,
324         WS_OVERLAPPEDWINDOW,
325         40, 40,
326         640,
327         480,
328         NULL, NULL, win32.hInstance, NULL );
329     if ( !hWnd ) {
330         common->FatalError( "GLW_GetWGLExtensionsWithFakeWindow: Couldn't create fake window" );
331     }
332
333     HDC hDC = GetDC( hWnd );
334         HGLRC gRC = wglCreateContext( hDC );
335         wglMakeCurrent( hDC, gRC );
336         GLW_CheckWGLExtensions( hDC );
337         wglDeleteContext( gRC );
338         ReleaseDC( hWnd, hDC );
339
340     DestroyWindow( hWnd );
341     while ( GetMessage( &msg, NULL, 0, 0 ) ) {
342         TranslateMessage( &msg );
343         DispatchMessage( &msg );
344     }
345 }
346
347 //=============================================================================
348
349 /*
350 ====================
351 GLW_WM_CREATE
352 ====================
353 */
354 void GLW_WM_CREATE( HWND hWnd ) {
355 }
356
357
358
359 /*
360 ====================
361 GLW_InitDriver
362
363 Set the pixelformat for the window before it is
364 shown, and create the rendering context
365 ====================
366 */
367 static bool GLW_InitDriver( glimpParms_t parms ) {
368     PIXELFORMATDESCRIPTOR src = 
369         {
370                 sizeof(PIXELFORMATDESCRIPTOR),  // size of this pfd
371                 1,                                                              // version number
372                 PFD_DRAW_TO_WINDOW |                    // support window
373                 PFD_SUPPORT_OPENGL |                    // support OpenGL
374                 PFD_DOUBLEBUFFER,                               // double buffered
375                 PFD_TYPE_RGBA,                                  // RGBA type
376                 32,                                                             // 32-bit color depth
377                 0, 0, 0, 0, 0, 0,                               // color bits ignored
378                 8,                                                              // 8 bit destination alpha
379                 0,                                                              // shift bit ignored
380                 0,                                                              // no accumulation buffer
381                 0, 0, 0, 0,                                     // accum bits ignored
382                 24,                                                             // 24-bit z-buffer      
383                 8,                                                              // 8-bit stencil buffer
384                 0,                                                              // no auxiliary buffer
385                 PFD_MAIN_PLANE,                                 // main layer
386                 0,                                                              // reserved
387                 0, 0, 0                                                 // layer masks ignored
388     };
389
390         common->Printf( "Initializing OpenGL driver\n" );
391
392         //
393         // get a DC for our window if we don't already have one allocated
394         //
395         if ( win32.hDC == NULL ) {
396                 common->Printf( "...getting DC: " );
397
398                 if ( ( win32.hDC = GetDC( win32.hWnd ) ) == NULL ) {
399                         common->Printf( "^3failed^0\n" );
400                         return false;
401                 }
402                 common->Printf( "succeeded\n" );
403         }
404
405         // the multisample path uses the wgl 
406         if ( wglChoosePixelFormatARB && parms.multiSamples > 1 ) {
407                 int             iAttributes[20];
408                 FLOAT   fAttributes[] = {0, 0};
409                 UINT    numFormats;
410
411                 // FIXME: specify all the other stuff
412                 iAttributes[0] = WGL_SAMPLE_BUFFERS_ARB;
413                 iAttributes[1] = 1;
414                 iAttributes[2] = WGL_SAMPLES_ARB;
415                 iAttributes[3] = parms.multiSamples;
416                 iAttributes[4] = WGL_DOUBLE_BUFFER_ARB;
417                 iAttributes[5] = TRUE;
418                 iAttributes[6] = WGL_STENCIL_BITS_ARB;
419                 iAttributes[7] = 8;
420                 iAttributes[8] = WGL_DEPTH_BITS_ARB;
421                 iAttributes[9] = 24;
422                 iAttributes[10] = WGL_RED_BITS_ARB;
423                 iAttributes[11] = 8;
424                 iAttributes[12] = WGL_BLUE_BITS_ARB;
425                 iAttributes[13] = 8;
426                 iAttributes[14] = WGL_GREEN_BITS_ARB;
427                 iAttributes[15] = 8;
428                 iAttributes[16] = WGL_ALPHA_BITS_ARB;
429                 iAttributes[17] = 8;
430                 iAttributes[18] = 0;
431                 iAttributes[19] = 0;
432
433                 wglChoosePixelFormatARB( win32.hDC, iAttributes, fAttributes, 1, &win32.pixelformat, &numFormats );
434         } else {
435                 // this is the "classic" choose pixel format path
436
437                 // eventually we may need to have more fallbacks, but for
438                 // now, ask for everything
439                 if ( parms.stereo ) {
440                         common->Printf( "...attempting to use stereo\n" );
441                         src.dwFlags |= PFD_STEREO;
442                 }
443
444                 //
445                 // choose, set, and describe our desired pixel format.  If we're
446                 // using a minidriver then we need to bypass the GDI functions,
447                 // otherwise use the GDI functions.
448                 //
449                 if ( ( win32.pixelformat = ChoosePixelFormat( win32.hDC, &src ) ) == 0 ) {
450                         common->Printf( "...^3GLW_ChoosePFD failed^0\n");
451                         return false;
452                 }
453                 common->Printf( "...PIXELFORMAT %d selected\n", win32.pixelformat );
454         }
455
456         // get the full info
457         DescribePixelFormat( win32.hDC, win32.pixelformat, sizeof( win32.pfd ), &win32.pfd );
458         glConfig.colorBits = win32.pfd.cColorBits;
459         glConfig.depthBits = win32.pfd.cDepthBits;
460         glConfig.stencilBits = win32.pfd.cStencilBits;
461
462         // XP seems to set this incorrectly
463         if ( !glConfig.stencilBits ) {
464                 glConfig.stencilBits = 8;
465         }
466
467         // the same SetPixelFormat is used either way
468         if ( SetPixelFormat( win32.hDC, win32.pixelformat, &win32.pfd ) == FALSE ) {
469                 common->Printf( "...^3SetPixelFormat failed^0\n", win32.hDC );
470                 return false;
471         }
472
473         //
474         // startup the OpenGL subsystem by creating a context and making it current
475         //
476         common->Printf( "...creating GL context: " );
477         if ( ( win32.hGLRC = qwglCreateContext( win32.hDC ) ) == 0 ) {
478                 common->Printf( "^3failed^0\n" );
479                 return false;
480         }
481         common->Printf( "succeeded\n" );
482
483         common->Printf( "...making context current: " );
484         if ( !qwglMakeCurrent( win32.hDC, win32.hGLRC ) ) {
485                 qwglDeleteContext( win32.hGLRC );
486                 win32.hGLRC = NULL;
487                 common->Printf( "^3failed^0\n" );
488                 return false;
489         }
490         common->Printf( "succeeded\n" );
491
492         return true;
493 }
494
495 /*
496 ====================
497 GLW_CreateWindowClasses
498 ====================
499 */
500 static void GLW_CreateWindowClasses( void ) {
501         WNDCLASS wc;
502
503         //
504         // register the window class if necessary
505         //
506         if ( win32.windowClassRegistered ) {
507                 return;
508         }
509
510         memset( &wc, 0, sizeof( wc ) );
511
512         wc.style         = 0;
513         wc.lpfnWndProc   = (WNDPROC) MainWndProc;
514         wc.cbClsExtra    = 0;
515         wc.cbWndExtra    = 0;
516         wc.hInstance     = win32.hInstance;
517         wc.hIcon         = LoadIcon( win32.hInstance, MAKEINTRESOURCE(IDI_ICON1));
518         wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
519         wc.hbrBackground = (struct HBRUSH__ *)COLOR_GRAYTEXT;
520         wc.lpszMenuName  = 0;
521         wc.lpszClassName = WIN32_WINDOW_CLASS_NAME;
522
523         if ( !RegisterClass( &wc ) ) {
524                 common->FatalError( "GLW_CreateWindow: could not register window class" );
525         }
526         common->Printf( "...registered window class\n" );
527
528         // now register the fake window class that is only used
529         // to get wgl extensions
530         wc.style         = 0;
531         wc.lpfnWndProc   = (WNDPROC) FakeWndProc;
532         wc.cbClsExtra    = 0;
533         wc.cbWndExtra    = 0;
534         wc.hInstance     = win32.hInstance;
535         wc.hIcon         = LoadIcon( win32.hInstance, MAKEINTRESOURCE(IDI_ICON1));
536         wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
537         wc.hbrBackground = (struct HBRUSH__ *)COLOR_GRAYTEXT;
538         wc.lpszMenuName  = 0;
539         wc.lpszClassName = WIN32_FAKE_WINDOW_CLASS_NAME;
540
541         if ( !RegisterClass( &wc ) ) {
542                 common->FatalError( "GLW_CreateWindow: could not register window class" );
543         }
544         common->Printf( "...registered fake window class\n" );
545
546         win32.windowClassRegistered = true;
547 }
548
549 /*
550 =======================
551 GLW_CreateWindow
552
553 Responsible for creating the Win32 window.
554 If cdsFullscreen is true, it won't have a border
555 =======================
556 */
557 static bool GLW_CreateWindow( glimpParms_t parms ) {
558         int                             stylebits;
559         int                             x, y, w, h;
560         int                             exstyle;
561
562         //
563         // compute width and height
564         //
565         if ( parms.fullScreen ) {
566                 exstyle = WS_EX_TOPMOST;
567                 stylebits = WS_POPUP|WS_VISIBLE|WS_SYSMENU;
568
569                 x = 0;
570                 y = 0;
571                 w = parms.width;
572                 h = parms.height;
573         } else {
574                 RECT    r;
575
576                 // adjust width and height for window border
577                 r.bottom = parms.height;
578                 r.left = 0;
579                 r.top = 0;
580                 r.right = parms.width;
581
582                 exstyle = 0;
583                 stylebits = WINDOW_STYLE|WS_SYSMENU;
584                 AdjustWindowRect (&r, stylebits, FALSE);
585
586                 w = r.right - r.left;
587                 h = r.bottom - r.top;
588
589                 x = win32.win_xpos.GetInteger();
590                 y = win32.win_ypos.GetInteger();
591
592                 // adjust window coordinates if necessary 
593                 // so that the window is completely on screen
594                 if ( x + w > win32.desktopWidth ) {
595                         x = ( win32.desktopWidth - w );
596                 }
597                 if ( y + h > win32.desktopHeight ) {
598                         y = ( win32.desktopHeight - h );
599                 }
600                 if ( x < 0 ) {
601                         x = 0;
602                 }
603                 if ( y < 0 ) {
604                         y = 0;
605                 }
606         }
607
608         win32.hWnd = CreateWindowEx (
609                  exstyle, 
610                  WIN32_WINDOW_CLASS_NAME,
611                  GAME_NAME,
612                  stylebits,
613                  x, y, w, h,
614                  NULL,
615                  NULL,
616                  win32.hInstance,
617                  NULL);
618
619         if ( !win32.hWnd ) {
620                 common->Printf( "^3GLW_CreateWindow() - Couldn't create window^0\n" );
621                 return false;
622         }
623
624         ::SetTimer( win32.hWnd, 0, 100, NULL );
625
626         ShowWindow( win32.hWnd, SW_SHOW );
627         UpdateWindow( win32.hWnd );
628         common->Printf( "...created window @ %d,%d (%dx%d)\n", x, y, w, h );
629
630         if ( !GLW_InitDriver( parms ) ) {
631                 ShowWindow( win32.hWnd, SW_HIDE );
632                 DestroyWindow( win32.hWnd );
633                 win32.hWnd = NULL;
634                 return false;
635         }
636
637         SetForegroundWindow( win32.hWnd );
638         SetFocus( win32.hWnd );
639
640         glConfig.isFullscreen = parms.fullScreen;
641
642         return true;
643 }
644
645
646
647 static void PrintCDSError( int value ) {
648         switch ( value ) {
649         case DISP_CHANGE_RESTART:
650                 common->Printf( "restart required\n" );
651                 break;
652         case DISP_CHANGE_BADPARAM:
653                 common->Printf( "bad param\n" );
654                 break;
655         case DISP_CHANGE_BADFLAGS:
656                 common->Printf( "bad flags\n" );
657                 break;
658         case DISP_CHANGE_FAILED:
659                 common->Printf( "DISP_CHANGE_FAILED\n" );
660                 break;
661         case DISP_CHANGE_BADMODE:
662                 common->Printf( "bad mode\n" );
663                 break;
664         case DISP_CHANGE_NOTUPDATED:
665                 common->Printf( "not updated\n" );
666                 break;
667         default:
668                 common->Printf( "unknown error %d\n", value );
669                 break;
670         }
671 }
672
673
674 /*
675 ===================
676 GLW_SetFullScreen
677 ===================
678 */
679 static bool GLW_SetFullScreen( glimpParms_t parms ) {
680 #if 0
681         // for some reason, bounds checker claims that windows is
682         // writing past the bounds of dm in the get display frequency call
683         union {
684                 DEVMODE dm;
685                 byte    filler[1024];
686         } hack;
687 #endif
688         DEVMODE dm;
689         int             cdsRet;
690
691         DEVMODE         devmode;
692         int                     modeNum;
693         bool            matched;
694
695         // first make sure the user is not trying to select a mode that his card/monitor can't handle
696         matched = false;
697         for ( modeNum = 0 ; ; modeNum++ ) {
698                 if ( !EnumDisplaySettings( NULL, modeNum, &devmode ) ) {
699                         if ( matched ) {
700                                 // we got a resolution match, but not a frequency match
701                                 // so disable the frequency requirement
702                                 common->Printf( "...^3%dhz is unsupported at %dx%d^0\n", parms.displayHz, parms.width, parms.height );
703                                 parms.displayHz = 0;
704                                 break;
705                         }
706                         common->Printf( "...^3%dx%d is unsupported in 32 bit^0\n", parms.width, parms.height );
707                         return false;
708                 }
709                 if ( (int)devmode.dmPelsWidth >= parms.width
710                         && (int)devmode.dmPelsHeight >= parms.height
711                         && devmode.dmBitsPerPel == 32 ) {
712
713                         matched = true;
714
715                         if ( parms.displayHz == 0 || devmode.dmDisplayFrequency == parms.displayHz ) {
716                                 break;
717                         }
718                 }
719         }
720
721         memset( &dm, 0, sizeof( dm ) );
722         dm.dmSize = sizeof( dm );
723
724         dm.dmPelsWidth  = parms.width;
725         dm.dmPelsHeight = parms.height;
726         dm.dmBitsPerPel = 32;
727         dm.dmFields     = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
728
729         if ( parms.displayHz != 0 ) {
730                 dm.dmDisplayFrequency = parms.displayHz;
731                 dm.dmFields |= DM_DISPLAYFREQUENCY;
732         }
733         
734         common->Printf( "...calling CDS: " );
735         
736         // try setting the exact mode requested, because some drivers don't report
737         // the low res modes in EnumDisplaySettings, but still work
738         if ( ( cdsRet = ChangeDisplaySettings( &dm, CDS_FULLSCREEN ) ) == DISP_CHANGE_SUCCESSFUL ) {
739                 common->Printf( "ok\n" );
740                 win32.cdsFullscreen = true;
741                 return true;
742         }
743
744         //
745         // the exact mode failed, so scan EnumDisplaySettings for the next largest mode
746         //
747         common->Printf( "^3failed^0, " );
748         
749         PrintCDSError( cdsRet );
750
751         common->Printf( "...trying next higher resolution:" );
752         
753         // we could do a better matching job here...
754         for ( modeNum = 0 ; ; modeNum++ ) {
755                 if ( !EnumDisplaySettings( NULL, modeNum, &devmode ) ) {
756                         break;
757                 }
758                 if ( (int)devmode.dmPelsWidth >= parms.width
759                         && (int)devmode.dmPelsHeight >= parms.height
760                         && devmode.dmBitsPerPel == 32 ) {
761
762                         if ( ( cdsRet = ChangeDisplaySettings( &devmode, CDS_FULLSCREEN ) ) == DISP_CHANGE_SUCCESSFUL ) {
763                                 common->Printf( "ok\n" );
764                                 win32.cdsFullscreen = true;
765
766                                 return true;
767                         }
768                         break;
769                 }
770         }
771
772         common->Printf( "\n...^3no high res mode found^0\n" );
773         return false;
774 }
775
776
777
778 /*
779 ===================
780 GLimp_Init
781
782 This is the platform specific OpenGL initialization function.  It
783 is responsible for loading OpenGL, initializing it,
784 creating a window of the appropriate size, doing
785 fullscreen manipulations, etc.  Its overall responsibility is
786 to make sure that a functional OpenGL subsystem is operating
787 when it returns to the ref.
788
789 If there is any failure, the renderer will revert back to safe
790 parameters and try again.
791 ===================
792 */
793 bool GLimp_Init( glimpParms_t parms ) {
794         const char      *driverName;
795         HDC             hDC;
796
797         common->Printf( "Initializing OpenGL subsystem\n" );
798
799         // check our desktop attributes
800         hDC = GetDC( GetDesktopWindow() );
801         win32.desktopBitsPixel = GetDeviceCaps( hDC, BITSPIXEL );
802         win32.desktopWidth = GetDeviceCaps( hDC, HORZRES );
803         win32.desktopHeight = GetDeviceCaps( hDC, VERTRES );
804         ReleaseDC( GetDesktopWindow(), hDC );
805
806         // we can't run in a window unless it is 32 bpp
807         if ( win32.desktopBitsPixel < 32 && !parms.fullScreen ) {
808                 common->Printf("^3Windowed mode requires 32 bit desktop depth^0\n");
809                 return false;
810         }
811
812         // save the hardware gamma so it can be
813         // restored on exit
814         GLimp_SaveGamma();
815
816         // create our window classes if we haven't already
817         GLW_CreateWindowClasses();
818
819         // this will load the dll and set all our qgl* function pointers,
820         // but doesn't create a window
821
822         // r_glDriver is only intended for using instrumented OpenGL
823         // dlls.  Normal users should never have to use it, and it is
824         // not archived.
825         driverName = r_glDriver.GetString()[0] ? r_glDriver.GetString() : "opengl32";
826         if ( !QGL_Init( driverName ) ) {
827                 common->Printf( "^3GLimp_Init() could not load r_glDriver \"%s\"^0\n", driverName );
828                 return false;
829         }
830
831         // getting the wgl extensions involves creating a fake window to get a context,
832         // which is pretty disgusting, and seems to mess with the AGP VAR allocation
833         GLW_GetWGLExtensionsWithFakeWindow();
834
835         // try to change to fullscreen
836         if ( parms.fullScreen ) {
837                 if ( !GLW_SetFullScreen( parms ) ) {
838                         GLimp_Shutdown();
839                         return false;
840                 }
841         }
842
843         // try to create a window with the correct pixel format
844         // and init the renderer context
845         if ( !GLW_CreateWindow( parms ) ) {
846                 GLimp_Shutdown();
847                 return false;
848         }
849
850         // wglSwapinterval, etc
851         GLW_CheckWGLExtensions( win32.hDC );
852
853         // check logging
854         GLimp_EnableLogging( ( r_logFile.GetInteger() != 0 ) );
855
856         return true;
857 }
858
859
860 /*
861 ===================
862 GLimp_SetScreenParms
863
864 Sets up the screen based on passed parms.. 
865 ===================
866 */
867 bool GLimp_SetScreenParms( glimpParms_t parms ) {
868         int exstyle;
869         int stylebits;
870         int x, y, w, h;
871         DEVMODE dm;
872
873         memset( &dm, 0, sizeof( dm ) );
874         dm.dmSize = sizeof( dm );
875         dm.dmFields     = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
876         if ( parms.displayHz != 0 ) {
877                 dm.dmDisplayFrequency = parms.displayHz;
878                 dm.dmFields |= DM_DISPLAYFREQUENCY;
879         }
880
881         win32.cdsFullscreen = parms.fullScreen;
882         glConfig.isFullscreen = parms.fullScreen;
883
884         if ( parms.fullScreen ) {
885                 exstyle = WS_EX_TOPMOST;
886                 stylebits = WS_POPUP|WS_VISIBLE|WS_SYSMENU;
887                 SetWindowLong( win32.hWnd, GWL_STYLE, stylebits );
888                 SetWindowLong( win32.hWnd, GWL_EXSTYLE, exstyle );
889                 dm.dmPelsWidth  = parms.width;
890                 dm.dmPelsHeight = parms.height;
891                 dm.dmBitsPerPel = 32;
892                 x = y = w = h = 0;
893         } else {
894                 RECT    r;
895
896                 // adjust width and height for window border
897                 r.bottom = parms.height;
898                 r.left = 0;
899                 r.top = 0;
900                 r.right = parms.width;
901
902                 w = r.right - r.left;
903                 h = r.bottom - r.top;
904
905                 x = win32.win_xpos.GetInteger();
906                 y = win32.win_ypos.GetInteger();
907
908                 // adjust window coordinates if necessary 
909                 // so that the window is completely on screen
910                 if ( x + w > win32.desktopWidth ) {
911                         x = ( win32.desktopWidth - w );
912                 }
913                 if ( y + h > win32.desktopHeight ) {
914                         y = ( win32.desktopHeight - h );
915                 }
916                 if ( x < 0 ) {
917                         x = 0;
918                 }
919                 if ( y < 0 ) {
920                         y = 0;
921                 }
922                 dm.dmPelsWidth  = win32.desktopWidth;
923                 dm.dmPelsHeight = win32.desktopHeight;
924                 dm.dmBitsPerPel = win32.desktopBitsPixel;
925                 exstyle = 0;
926                 stylebits = WINDOW_STYLE|WS_SYSMENU;
927                 AdjustWindowRect (&r, stylebits, FALSE);
928                 SetWindowLong( win32.hWnd, GWL_STYLE, stylebits );
929                 SetWindowLong( win32.hWnd, GWL_EXSTYLE, exstyle );
930                 common->Printf( "%i %i %i %i\n", x, y, w, h );
931         }
932         bool ret = ( ChangeDisplaySettings( &dm, parms.fullScreen ? CDS_FULLSCREEN : 0 ) == DISP_CHANGE_SUCCESSFUL );
933         SetWindowPos( win32.hWnd, parms.fullScreen ? HWND_TOPMOST : HWND_NOTOPMOST, x, y, w, h, parms.fullScreen ? SWP_NOSIZE | SWP_NOMOVE : SWP_SHOWWINDOW );
934         return ret;
935 }
936
937 /*
938 ===================
939 GLimp_Shutdown
940
941 This routine does all OS specific shutdown procedures for the OpenGL
942 subsystem.
943 ===================
944 */
945 void GLimp_Shutdown( void ) {
946         const char *success[] = { "failed", "success" };
947         int retVal;
948
949         common->Printf( "Shutting down OpenGL subsystem\n" );
950
951         // set current context to NULL
952         if ( qwglMakeCurrent ) {
953                 retVal = qwglMakeCurrent( NULL, NULL ) != 0;
954                 common->Printf( "...wglMakeCurrent( NULL, NULL ): %s\n", success[retVal] );
955         }
956
957         // delete HGLRC
958         if ( win32.hGLRC ) {
959                 retVal = qwglDeleteContext( win32.hGLRC ) != 0;
960                 common->Printf( "...deleting GL context: %s\n", success[retVal] );
961                 win32.hGLRC = NULL;
962         }
963
964         // release DC
965         if ( win32.hDC ) {
966                 retVal = ReleaseDC( win32.hWnd, win32.hDC ) != 0;
967                 common->Printf( "...releasing DC: %s\n", success[retVal] );
968                 win32.hDC   = NULL;
969         }
970
971         // destroy window
972         if ( win32.hWnd ) {
973                 common->Printf( "...destroying window\n" );
974                 ShowWindow( win32.hWnd, SW_HIDE );
975                 DestroyWindow( win32.hWnd );
976                 win32.hWnd = NULL;
977         }
978
979         // reset display settings
980         if ( win32.cdsFullscreen ) {
981                 common->Printf( "...resetting display\n" );
982                 ChangeDisplaySettings( 0, 0 );
983                 win32.cdsFullscreen = false;
984         }
985
986         // close the thread so the handle doesn't dangle
987         if ( win32.renderThreadHandle ) {
988                 common->Printf( "...closing smp thread\n" );
989                 CloseHandle( win32.renderThreadHandle );
990                 win32.renderThreadHandle = NULL;
991         }
992
993         // restore gamma
994         GLimp_RestoreGamma();
995
996         // shutdown QGL subsystem
997         QGL_Shutdown();
998 }
999
1000
1001 /*
1002 =====================
1003 GLimp_SwapBuffers
1004 =====================
1005 */
1006 void GLimp_SwapBuffers( void ) {
1007         //
1008         // wglSwapinterval is a windows-private extension,
1009         // so we must check for it here instead of portably
1010         //
1011         if ( r_swapInterval.IsModified() ) {
1012                 r_swapInterval.ClearModified();
1013
1014                 if ( wglSwapIntervalEXT ) {
1015                         wglSwapIntervalEXT( r_swapInterval.GetInteger() );
1016                 }
1017         }
1018
1019         qwglSwapBuffers( win32.hDC );
1020
1021 //Sys_DebugPrintf( "*** SwapBuffers() ***\n" );
1022 }
1023
1024 /*
1025 ===========================================================
1026
1027 SMP acceleration
1028
1029 ===========================================================
1030 */
1031
1032 //#define       REALLOC_DC
1033
1034 /*
1035 ===================
1036 GLimp_ActivateContext
1037
1038 ===================
1039 */
1040 void GLimp_ActivateContext( void ) {
1041         if ( !qwglMakeCurrent( win32.hDC, win32.hGLRC ) ) {
1042                 win32.wglErrors++;
1043         }
1044 }
1045
1046 /*
1047 ===================
1048 GLimp_DeactivateContext
1049
1050 ===================
1051 */
1052 void GLimp_DeactivateContext( void ) {
1053         qglFinish();
1054         if ( !qwglMakeCurrent( win32.hDC, NULL ) ) {
1055                 win32.wglErrors++;
1056         }
1057 #ifdef REALLOC_DC
1058         // makeCurrent NULL frees the DC, so get another
1059         if ( ( win32.hDC = GetDC( win32.hWnd ) ) == NULL ) {
1060                 win32.wglErrors++;
1061         }
1062 #endif
1063
1064 }
1065
1066 /*
1067 ===================
1068 GLimp_RenderThreadWrapper
1069
1070 ===================
1071 */
1072 static void GLimp_RenderThreadWrapper( void ) {
1073         win32.glimpRenderThread();
1074
1075         // unbind the context before we die
1076         qwglMakeCurrent( win32.hDC, NULL );
1077 }
1078
1079 /*
1080 =======================
1081 GLimp_SpawnRenderThread
1082
1083 Returns false if the system only has a single processor
1084 =======================
1085 */
1086 bool GLimp_SpawnRenderThread( void (*function)( void ) ) {
1087         SYSTEM_INFO info;
1088
1089         // check number of processors
1090         GetSystemInfo( &info );
1091         if ( info.dwNumberOfProcessors < 2 ) {
1092                 return false;
1093         }
1094         
1095         // create the IPC elements
1096         win32.renderCommandsEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
1097         win32.renderCompletedEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
1098         win32.renderActiveEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
1099
1100         win32.glimpRenderThread = function;
1101
1102         win32.renderThreadHandle = CreateThread(
1103            NULL,        // LPSECURITY_ATTRIBUTES lpsa,
1104            0,           // DWORD cbStack,
1105            (LPTHREAD_START_ROUTINE)GLimp_RenderThreadWrapper,   // LPTHREAD_START_ROUTINE lpStartAddr,
1106            0,                   // LPVOID lpvThreadParm,
1107            0,                   //   DWORD fdwCreate,
1108            &win32.renderThreadId );
1109
1110         if ( !win32.renderThreadHandle ) {
1111                 common->Error( "GLimp_SpawnRenderThread: failed" );
1112         }
1113
1114         SetThreadPriority( win32.renderThreadHandle, THREAD_PRIORITY_ABOVE_NORMAL );
1115 #if 0
1116         // make sure they always run on different processors
1117         SetThreadAffinityMask( GetCurrentThread, 1 );
1118         SetThreadAffinityMask( win32.renderThreadHandle, 2 );
1119 #endif
1120
1121         return true;
1122 }
1123
1124
1125 //#define       DEBUG_PRINTS
1126
1127 /*
1128 ===================
1129 GLimp_BackEndSleep
1130
1131 ===================
1132 */
1133 void *GLimp_BackEndSleep( void ) {
1134         void    *data;
1135
1136 #ifdef DEBUG_PRINTS
1137 OutputDebugString( "-->GLimp_BackEndSleep\n" );
1138 #endif
1139         ResetEvent( win32.renderActiveEvent );
1140
1141         // after this, the front end can exit GLimp_FrontEndSleep
1142         SetEvent( win32.renderCompletedEvent );
1143
1144         WaitForSingleObject( win32.renderCommandsEvent, INFINITE );
1145
1146         ResetEvent( win32.renderCompletedEvent );
1147         ResetEvent( win32.renderCommandsEvent );
1148
1149         data = win32.smpData;
1150
1151         // after this, the main thread can exit GLimp_WakeRenderer
1152         SetEvent( win32.renderActiveEvent );
1153
1154 #ifdef DEBUG_PRINTS
1155 OutputDebugString( "<--GLimp_BackEndSleep\n" );
1156 #endif
1157         return data;
1158 }
1159
1160 /*
1161 ===================
1162 GLimp_FrontEndSleep
1163
1164 ===================
1165 */
1166 void GLimp_FrontEndSleep( void ) {
1167 #ifdef DEBUG_PRINTS
1168 OutputDebugString( "-->GLimp_FrontEndSleep\n" );
1169 #endif
1170         WaitForSingleObject( win32.renderCompletedEvent, INFINITE );
1171
1172 #ifdef DEBUG_PRINTS
1173 OutputDebugString( "<--GLimp_FrontEndSleep\n" );
1174 #endif
1175 }
1176
1177 volatile bool   renderThreadActive;
1178
1179 /*
1180 ===================
1181 GLimp_WakeBackEnd
1182
1183 ===================
1184 */
1185 void GLimp_WakeBackEnd( void *data ) {
1186         int             r;
1187
1188 #ifdef DEBUG_PRINTS
1189 OutputDebugString( "-->GLimp_WakeBackEnd\n" );
1190 #endif
1191         win32.smpData = data;
1192
1193         if ( renderThreadActive ) {
1194                 common->FatalError( "GLimp_WakeBackEnd: already active" );
1195         }
1196
1197         r = WaitForSingleObject( win32.renderActiveEvent, 0 );
1198         if ( r == WAIT_OBJECT_0 ) {
1199                 common->FatalError( "GLimp_WakeBackEnd: already signaled" );
1200         }
1201
1202         r = WaitForSingleObject( win32.renderCommandsEvent, 0 );
1203         if ( r == WAIT_OBJECT_0 ) {
1204                 common->FatalError( "GLimp_WakeBackEnd: commands already signaled" );
1205         }
1206
1207         // after this, the renderer can continue through GLimp_RendererSleep
1208         SetEvent( win32.renderCommandsEvent );
1209
1210         r = WaitForSingleObject( win32.renderActiveEvent, 5000 );
1211
1212         if ( r == WAIT_TIMEOUT ) {
1213                 common->FatalError( "GLimp_WakeBackEnd: WAIT_TIMEOUT" );
1214         }
1215
1216 #ifdef DEBUG_PRINTS
1217 OutputDebugString( "<--GLimp_WakeBackEnd\n" );
1218 #endif
1219 }
1220
1221 //===================================================================
1222
1223 /*
1224 ===================
1225 GLimp_ExtensionPointer
1226
1227 Returns a function pointer for an OpenGL extension entry point
1228 ===================
1229 */
1230 GLExtension_t GLimp_ExtensionPointer( const char *name ) {
1231         void    (*proc)(void);
1232
1233         proc = (GLExtension_t)qwglGetProcAddress( name );
1234
1235         if ( !proc ) {
1236                 common->Printf( "Couldn't find proc address for: %s\n", name );
1237         }
1238
1239         return proc;
1240 }
1241