1 /*
2 Copyright (C) 1997-2001 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 /*
21 ** GLW_IMP.C
22 **
23 ** This file contains ALL Win32 specific stuff having to do with the
24 ** OpenGL refresh. When a port is being made the following functions
25 ** must be implemented by the port:
26 **
27 ** GLimp_EndFrame
28 ** GLimp_Init
29 ** GLimp_Shutdown
30 ** GLimp_SwitchFullscreen
31 **
32 */
33
34 #include "win_local.h"
35 #include "win_glimp.h"
36 #include "win_wgl.h"
37
38 glwstate_t glw_state;
39
40 static cvar_t *glw_driver;
41 static cvar_t *glw_allow_software;
42 static cvar_t *glw_drawbuffer;
43 static cvar_t *glw_bitdepth;
44 static cvar_t *glw_stereo;
45 static cvar_t *vid_flip_on_switch;
46 static cvar_t *vid_displayfrequency;
47
GLimp_FatalShutdown(void)48 void GLimp_FatalShutdown( void ) {
49 if( glw_state.flags & QVF_GAMMARAMP_ENABLED ) {
50 if( glw_state.hDC ) {
51 SetDeviceGammaRamp( glw_state.hDC, glw_state.originalGammaRamp );
52 }
53 glw_state.flags &= ~QVF_GAMMARAMP_ENABLED;
54 }
55
56 if( glw_state.hWnd ) {
57 DestroyWindow( glw_state.hWnd );
58 glw_state.hWnd = NULL;
59 }
60
61 if( glw_state.flags & QVF_FULLSCREEN_ENABLED ) {
62 ChangeDisplaySettings( 0, 0 );
63 glw_state.flags &= ~QVF_FULLSCREEN_ENABLED;
64 }
65 }
66
67 /*
68 ** GLimp_Shutdown
69 **
70 ** This routine does all OS specific shutdown procedures for the OpenGL
71 ** subsystem. Under OpenGL this means NULLing out the current DC and
72 ** HGLRC, deleting the rendering context, and releasing the DC acquired
73 ** for the window. The state structure is also nulled out.
74 **
75 */
GLimp_Shutdown(void)76 static void GLimp_Shutdown( void ) {
77 Com_Printf( "Shutting down OpenGL subsystem...\n" );
78
79 if( glw_state.flags & QVF_GAMMARAMP_ENABLED ) {
80 if( glw_state.hDC ) {
81 Com_Printf( "...restoring gamma settings: " );
82 if( !SetDeviceGammaRamp( glw_state.hDC, glw_state.originalGammaRamp ) ) {
83 Com_Printf( "failed\n" );
84 } else {
85 Com_Printf( "ok\n" );
86 }
87 }
88 glw_state.flags &= ~QVF_GAMMARAMP_ENABLED;
89 }
90
91 if( qwglMakeCurrent ) {
92 Com_Printf( "...making context not current: " );
93 if( !qwglMakeCurrent( NULL, NULL ) ) {
94 Com_Printf( "failed\n" );
95 } else {
96 Com_Printf( "ok\n" );
97 }
98 }
99
100 if( glw_state.hGLRC ) {
101 Com_Printf( "...deleting OpenGL context: " );
102 if( !qwglDeleteContext( glw_state.hGLRC ) ) {
103 Com_Printf( "failed\n" );
104 } else {
105 Com_Printf( "ok\n" );
106 }
107 glw_state.hGLRC = NULL;
108 }
109
110 if( glw_state.hDC ) {
111 Com_Printf( "...releasing DC: " );
112 if( !ReleaseDC( glw_state.hWnd, glw_state.hDC ) ) {
113 Com_Printf( "failed\n" );
114 } else {
115 Com_Printf( "ok\n" );
116 }
117 glw_state.hDC = NULL;
118 }
119
120 if( glw_state.hWnd ) {
121 Com_Printf( "...destroying window\n" );
122 Win_DestroyWindow( glw_state.hWnd );
123 glw_state.hWnd = NULL;
124 }
125
126 if( glw_state.flags & QVF_FULLSCREEN_ENABLED ) {
127 ChangeDisplaySettings( 0, 0 );
128 glw_state.flags &= ~QVF_FULLSCREEN_ENABLED;
129 }
130
131 WGL_Shutdown();
132 }
133
134
135 /*
136 ** GLimp_Init
137 **
138 ** This routine is responsible for initializing the OS specific portions
139 ** of OpenGL. Under Win32 this means dealing with the pixelformats and
140 ** doing the wgl interface stuff.
141 */
GLimp_Init(void)142 static qboolean GLimp_Init( void ) {
143 OSVERSIONINFO vinfo;
144
145 // Com_Printf( "Initializing OpenGL subsystem\n" );
146
147 Win_Init();
148
149 vinfo.dwOSVersionInfoSize = sizeof( vinfo );
150
151 if( !GetVersionEx( &vinfo ) ) {
152 Com_Error( ERR_FATAL, "GLimp_Init: GetVersionEx failed" );
153 }
154
155 glw_state.allowdisplaydepthchange = qfalse;
156 if( vinfo.dwMajorVersion > 4 ) {
157 glw_state.allowdisplaydepthchange = qtrue;
158 } else if ( vinfo.dwMajorVersion == 4 ) {
159 if( vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT ) {
160 glw_state.allowdisplaydepthchange = qtrue;
161 } else if( vinfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) {
162 if( LOWORD( vinfo.dwBuildNumber ) >= 1111 ) {
163 glw_state.allowdisplaydepthchange = qtrue;
164 }
165 }
166 }
167
168 Cvar_Subsystem( CVAR_SYSTEM_VIDEO );
169
170 glw_driver = Cvar_Get( "gl_driver", "opengl32", CVAR_ARCHIVE|CVAR_LATCHED );
171 glw_drawbuffer = Cvar_Get( "gl_drawbuffer", "GL_BACK", 0 );
172 glw_bitdepth = Cvar_Get( "gl_bitdepth", "0", CVAR_LATCHED );
173 glw_allow_software = Cvar_Get( "gl_allow_software", "0", CVAR_LATCHED );
174 glw_stereo = Cvar_Get( "cl_stereo", "0", CVAR_LATCHED );
175 vid_flip_on_switch = Cvar_Get( "vid_flip_on_switch", "0", 0 );
176 vid_displayfrequency = Cvar_Get( "vid_displayfrequency", "0", CVAR_LATCHED );
177
178 Cvar_Subsystem( CVAR_SYSTEM_GENERIC );
179
180 return qtrue;
181 }
182
GLimp_VerifyDriver(void)183 static qboolean GLimp_VerifyDriver( void ) {
184 char buffer[MAX_STRING_CHARS];
185
186 Q_strncpyz( buffer, qglGetString( GL_RENDERER ), sizeof( buffer ) );
187 Q_strlwr( buffer );
188 if( strcmp( buffer, "gdi generic" ) == 0 )
189 if( !( glw_state.flags & QVF_ACCELERATED ) )
190 return qfalse;
191 return qtrue;
192 }
193
GLimp_InitGL(int width,int height,qboolean fullscreen)194 static qboolean GLimp_InitGL( int width, int height, qboolean fullscreen ) {
195 PIXELFORMATDESCRIPTOR pfd = {
196 sizeof( PIXELFORMATDESCRIPTOR ), // size of this pfd
197 1, // version number
198 PFD_DRAW_TO_WINDOW | // support window
199 PFD_SUPPORT_OPENGL | // support OpenGL
200 PFD_DOUBLEBUFFER, // double buffered
201 PFD_TYPE_RGBA, // RGBA type
202 24, // 24-bit color depth
203 0, 0, 0, 0, 0, 0, // color bits ignored
204 0, // no alpha buffer
205 0, // shift bit ignored
206 0, // no accumulation buffer
207 0, 0, 0, 0, // accum bits ignored
208 32, // 32-bit z-buffer
209 0, // no stencil buffer
210 0, // no auxiliary buffer
211 PFD_MAIN_PLANE, // main layer
212 0, // reserved
213 0, 0, 0 // layer masks ignored
214 };
215 int pixelformat;
216 DWORD dwLastError = 0;
217
218 glw_state.hWnd = Win_CreateWindow( width, height, fullscreen );
219 glw_state.flags = 0;
220
221 /*
222 ** set PFD_STEREO if necessary
223 */
224 if( glw_stereo->integer ) {
225 Com_Printf( "...attempting to use stereo\n" );
226 pfd.dwFlags |= PFD_STEREO;
227 glw_state.flags |= QVF_STEREO_ENABLED;
228 }
229
230 /*
231 ** figure out if we're running on a minidriver or not
232 */
233 if( !strstr( glw_driver->string, "opengl32" ) ) {
234 Com_Printf( "...running a minidriver: %s\n", glw_driver->string );
235 glw_state.flags |= QVF_MINIDRIVER;
236 }
237
238 /*
239 ** Load OpenGL library
240 */
241 Com_Printf( "...initializing WGL: " );
242 if( !WGL_Init( glw_driver->string ) ) {
243 dwLastError = GetLastError();
244 Com_Printf( "failed\n" );
245 goto fail;
246 }
247 Com_Printf( "ok\n" );
248
249 /*
250 ** Get a DC for the specified window
251 */
252 Com_Printf( "...getting DC: " );
253 if( ( glw_state.hDC = GetDC( glw_state.hWnd ) ) == NULL ) {
254 dwLastError = GetLastError();
255 Com_Printf( "failed\n" );
256 goto fail;
257 }
258 Com_Printf( "ok\n" );
259
260 Com_Printf( "...setting pixel format: " );
261 if( glw_state.flags & QVF_MINIDRIVER ) {
262 if ( !( pixelformat = qwglChoosePixelFormat( glw_state.hDC, &pfd ) ) ) {
263 dwLastError = GetLastError();
264 Com_Printf( "failed\n" );
265 goto fail;
266 }
267
268 if( qwglSetPixelFormat( glw_state.hDC, pixelformat, &pfd ) == FALSE ) {
269 dwLastError = GetLastError();
270 Com_Printf( "failed\n" );
271 goto fail;
272 }
273
274 qwglDescribePixelFormat( glw_state.hDC, pixelformat,
275 sizeof( pfd ), &pfd );
276 } else {
277 if( ( pixelformat = ChoosePixelFormat( glw_state.hDC, &pfd ) ) == 0 ) {
278 dwLastError = GetLastError();
279 Com_Printf( "failed\n" );
280 goto fail;
281 }
282
283 if( SetPixelFormat( glw_state.hDC, pixelformat, &pfd ) == FALSE ) {
284 dwLastError = GetLastError();
285 Com_Printf( "failed\n" );
286 goto fail;
287 }
288
289 DescribePixelFormat( glw_state.hDC, pixelformat, sizeof( pfd ), &pfd );
290
291 if( ( pfd.dwFlags & PFD_GENERIC_ACCELERATED ) ||
292 glw_allow_software->integer )
293 {
294 glw_state.flags |= QVF_ACCELERATED;
295 }
296 }
297 Com_Printf( "ok\n" );
298
299 /*
300 ** report if stereo is desired but unavailable
301 */
302 if( !( pfd.dwFlags & PFD_STEREO ) &&
303 ( glw_state.flags & QVF_STEREO_ENABLED ) )
304 {
305 Com_Printf( "...failed to select stereo pixel format\n" );
306 Cvar_SetInteger( "cl_stereo", 0 );
307 glw_state.flags &= ~QVF_STEREO_ENABLED;
308 }
309
310 /*
311 ** startup the OpenGL subsystem by creating a context and making
312 ** it current
313 */
314 Com_Printf( "...creating OpenGL context: " );
315 if( ( glw_state.hGLRC = qwglCreateContext( glw_state.hDC ) ) == NULL ) {
316 dwLastError = GetLastError();
317 Com_Printf( "failed\n" );
318 goto fail;
319 }
320 Com_Printf( "ok\n" );
321
322 Com_Printf( "...making context current: " );
323 if( !qwglMakeCurrent( glw_state.hDC, glw_state.hGLRC ) ) {
324 dwLastError = GetLastError();
325 Com_Printf( "failed\n" );
326 goto fail;
327 }
328 Com_Printf( "ok\n" );
329
330 if( !GLimp_VerifyDriver() ) {
331 Com_Printf( "No hardware acceleration detected\n" );
332 goto fail;
333 }
334
335 /*
336 ** print out PFD specifics
337 */
338 Com_Printf( "GL_VENDOR: %s\n", qglGetString( GL_VENDOR ) );
339 Com_Printf( "GL_RENDERER: %s\n", qglGetString( GL_RENDERER ) );
340 Com_Printf( "GL_PFD: color(%d-bits: %d,%d,%d,%d) Z(%d-bit) stencil(%d-bit)\n",
341 pfd.cColorBits, pfd.cRedBits, pfd.cGreenBits, pfd.cBlueBits,
342 pfd.cAlphaBits, pfd.cDepthBits, pfd.cStencilBits );
343
344
345 if( GetDeviceGammaRamp( glw_state.hDC, glw_state.originalGammaRamp ) ) {
346 glw_state.flags |= QVF_GAMMARAMP_ENABLED;
347 memcpy( glw_state.customGammaRamp, glw_state.originalGammaRamp,
348 sizeof( glw_state.customGammaRamp ) );
349 }
350
351 return qtrue;
352
353 fail:
354 if( dwLastError ) {
355 Com_Printf( "GetLastError() = 0x%lx\n", dwLastError );
356 }
357
358 if( glw_state.hWnd ) {
359 Win_DestroyWindow( glw_state.hWnd );
360 glw_state.hWnd = NULL;
361 }
362
363 if( glw_state.hGLRC && qwglDeleteContext ) {
364 qwglDeleteContext( glw_state.hGLRC );
365 glw_state.hGLRC = NULL;
366 }
367
368 if( glw_state.hDC ) {
369 ReleaseDC( glw_state.hWnd, glw_state.hDC );
370 glw_state.hDC = NULL;
371 }
372
373 WGL_Shutdown();
374
375 return qfalse;
376 }
377
378 /*
379 ** GLimp_SetMode
380 */
381
382 // get the list of acceptable video modes
383 #include "vid_modes.h"
384
GLimp_SetMode(int * pwidth,int * pheight,int mode,qboolean fullscreen)385 static vidSetModeResult_t GLimp_SetMode( int *pwidth, int *pheight,
386 int mode, qboolean fullscreen )
387 {
388 int width, height;
389 const char *win_fs[] = { "W", "FS" };
390 DEVMODE dm;
391 qboolean success;
392
393 Com_Printf( "Initializing OpenGL display\n" );
394
395 if( mode == -1 ) {
396 Com_Printf( "...setting custom mode: " );
397 width = *pwidth;
398 height = *pheight;
399 } else {
400 Com_Printf( "...setting mode %d: ", mode );
401 if( !VID_GetModeInfo( &width, &height, mode ) ) {
402 Com_Printf( "invalid mode\n" );
403 return SETMODE_ERROR;
404 }
405 *pwidth = width;
406 *pheight = height;
407 }
408
409 Com_Printf( "%d %d %s\n", width, height, win_fs[fullscreen] );
410
411 // destroy the existing window
412 if( glw_state.hWnd ) {
413 GLimp_Shutdown();
414 }
415
416 if( !fullscreen ) {
417 Com_Printf( "...setting windowed mode\n" );
418 goto windowed;
419 }
420
421 // do a CDS if needed
422 Com_Printf( "...attempting fullscreen\n" );
423
424 if( glw_bitdepth->integer && !glw_state.allowdisplaydepthchange ) {
425 Com_Printf( "gl_bitdepth requires Win95 OSR2.x or WinNT 4.x\n" );
426 Cvar_SetInteger( "gl_bitdepth", 0 );
427 }
428
429 memset( &dm, 0, sizeof( dm ) );
430
431 dm.dmSize = sizeof( dm );
432
433 dm.dmPelsWidth = width;
434 dm.dmPelsHeight = height;
435 dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
436
437 if( vid_displayfrequency->integer ) {
438 dm.dmDisplayFrequency = vid_displayfrequency->integer;
439 dm.dmFields |= DM_DISPLAYFREQUENCY;
440 Com_Printf( "...using display frequency of %d\n",
441 vid_displayfrequency->integer );
442 }
443
444 if( glw_bitdepth->integer ) {
445 dm.dmBitsPerPel = glw_bitdepth->integer;
446 dm.dmFields |= DM_BITSPERPEL;
447 Com_Printf( "...using gl_bitdepth of %d\n",
448 glw_bitdepth->integer );
449 } else {
450 HDC hdc;
451 int bitspixel;
452
453 hdc = GetDC( NULL );
454 bitspixel = GetDeviceCaps( hdc, BITSPIXEL );
455 ReleaseDC( NULL, hdc );
456
457 Com_Printf( "...using desktop display depth of %d\n", bitspixel );
458 }
459
460 Com_Printf( "...calling CDS: " );
461 if( ChangeDisplaySettings( &dm, CDS_FULLSCREEN ) == DISP_CHANGE_SUCCESSFUL ) {
462 Com_Printf( "ok\n" );
463
464 success = GLimp_InitGL( width, height, qtrue );
465 if( !success && ( glw_state.flags & QVF_MINIDRIVER ) ) {
466 Com_Printf( "...attempting to load opengl32\n" );
467 Cvar_Set( "gl_driver","opengl32" );
468 success = GLimp_InitGL( width, height, qtrue );
469 }
470 if( !success ) {
471 return SETMODE_ERROR;
472 }
473
474 glw_state.flags |= QVF_FULLSCREEN_ENABLED;
475 glw_state.fullscreenDM = dm;
476 return SETMODE_FULLSCREEN;
477 }
478 Com_Printf( "failed\n" );
479
480 dm.dmPelsWidth = width * 2;
481 dm.dmPelsHeight = height;
482 dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
483
484 if( vid_displayfrequency->integer ) {
485 dm.dmDisplayFrequency = vid_displayfrequency->integer;
486 dm.dmFields |= DM_DISPLAYFREQUENCY;
487 }
488
489 if( glw_bitdepth->integer ) {
490 dm.dmBitsPerPel = glw_bitdepth->integer;
491 dm.dmFields |= DM_BITSPERPEL;
492 }
493
494 Com_Printf( "...calling CDS assuming dual monitors: " );
495 if( ChangeDisplaySettings( &dm, CDS_FULLSCREEN ) == DISP_CHANGE_SUCCESSFUL ) {
496 Com_Printf( "ok\n" );
497
498 success = GLimp_InitGL( width, height, qtrue );
499 if( !success && ( glw_state.flags & QVF_MINIDRIVER ) ) {
500 Com_Printf( "...attempting to load opengl32\n" );
501 Cvar_Set( "gl_driver","opengl32" );
502 success = GLimp_InitGL( width, height, qtrue );
503 }
504 if( !success ) {
505 return SETMODE_ERROR;
506 }
507
508 glw_state.flags |= QVF_FULLSCREEN_ENABLED;
509 glw_state.fullscreenDM = dm;
510 return SETMODE_FULLSCREEN;
511 }
512 Com_Printf( "failed\n" );
513
514 Com_Printf( "...falling back to windowed mode\n" );
515
516 windowed:
517 ChangeDisplaySettings( NULL, 0 );
518
519 success = GLimp_InitGL( width, height, qfalse );
520 if( !success && ( glw_state.flags & QVF_MINIDRIVER ) ) {
521 Com_Printf( "...attempting to load opengl32\n" );
522 Cvar_Set( "gl_driver","opengl32" );
523 success = GLimp_InitGL( width, height, qfalse );
524 }
525
526 return success ? SETMODE_WINDOWED : SETMODE_ERROR;
527 }
528
529 /*
530 ==============
531 GLimp_UpdateGamma
532 ==============
533 */
GLimp_UpdateGamma(const byte * table)534 static void GLimp_UpdateGamma( const byte *table ) {
535 WORD v;
536 int i;
537
538 if( !( glw_state.flags & QVF_GAMMARAMP_ENABLED ) ) {
539 return;
540 }
541
542 for( i = 0; i < 256; i++ ) {
543 v = table[i] << 8;
544 glw_state.customGammaRamp[0][i] = v;
545 glw_state.customGammaRamp[1][i] = v;
546 glw_state.customGammaRamp[2][i] = v;
547 }
548
549 SetDeviceGammaRamp( glw_state.hDC, glw_state.customGammaRamp );
550
551 }
552
553 /*
554 ** GLimp_BeginFrame
555 */
GLimp_BeginFrame(float camera_separation)556 static void GLimp_BeginFrame( float camera_separation ) {
557 //qglDrawBuffer( GL_BACK );
558 }
559
560 /*
561 ** GLimp_EndFrame
562 **
563 ** Responsible for doing a swapbuffers and possibly for other stuff
564 ** as yet to be determined. Probably better not to make this a GLimp
565 ** function and instead do a call to GLimp_SwapBuffers.
566 */
567
GLimp_EndFrame(void)568 static void GLimp_EndFrame( void ) {
569 /*if( Q_stricmp( glw_drawbuffer->string, "GL_BACK" ) == 0 ) {
570 if( !qwglSwapBuffers( glw_state.hDC ) )
571 Com_Error( ERR_FATAL, "GLimp_EndFrame: wglSwapBuffers failed" );
572 }
573 */
574 if( !qwglSwapBuffers( glw_state.hDC ) )
575 Com_Error( ERR_FATAL, "GLimp_EndFrame: wglSwapBuffers failed" );
576 }
577
578 /*
579 ============
580 GLimp_GetFlags
581 ============
582 */
GLimp_GetFlags(void)583 static vidFlags_t GLimp_GetFlags( void ) {
584 return glw_state.flags;
585 }
586
GLimp_AppActivate(qboolean active)587 void GLimp_AppActivate( qboolean active ) {
588 if( !glw_state.hDC ) {
589 return;
590 }
591 if( glw_state.flags & QVF_GAMMARAMP_ENABLED ) {
592 if( active ) {
593 SetDeviceGammaRamp( glw_state.hDC, glw_state.customGammaRamp );
594 } else {
595 SetDeviceGammaRamp( glw_state.hDC, glw_state.originalGammaRamp );
596 }
597 }
598
599 if( !( glw_state.flags & QVF_FULLSCREEN_ENABLED ) ) {
600 return;
601 }
602 if( !vid_flip_on_switch->integer ) {
603 return;
604 }
605 if( active ) {
606 ChangeDisplaySettings( &glw_state.fullscreenDM, CDS_FULLSCREEN );
607 } else {
608 ChangeDisplaySettings( NULL, 0 );
609 }
610
611 }
612
613 /*
614 @@@@@@@@@@@@
615 GLimp_FillAPI
616 @@@@@@@@@@@@
617 */
Video_FillGLAPI(vidGLAPI_t * api)618 void Video_FillGLAPI( vidGLAPI_t *api ) {
619 api->Init = GLimp_Init;
620 api->Shutdown = GLimp_Shutdown;
621 api->UpdateGamma = GLimp_UpdateGamma;
622 api->GetFlags = GLimp_GetFlags;
623 api->SetMode = GLimp_SetMode;
624 api->GetProcAddr = WGL_GetProcAddress;
625 api->BeginFrame = GLimp_BeginFrame;
626 api->EndFrame = GLimp_EndFrame;
627 }
628
629