]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/sys/osx/macosx_glimp.mm
hello world
[icculus/iodoom3.git] / neo / sys / osx / macosx_glimp.mm
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 // -*- mode: objc -*-
30 #import "../../idlib/precompiled.h"
31
32 #import "../../renderer/tr_local.h"
33
34 #import "macosx_glimp.h"
35
36 #import "macosx_local.h"
37 #import "macosx_sys.h"
38 #import "macosx_display.h"
39
40 #import <AppKit/AppKit.h>
41 #import <Foundation/Foundation.h>
42
43 #import <mach-o/dyld.h>
44 #import <mach/mach.h>
45 #import <mach/mach_error.h>
46
47 static idCVar r_minDisplayRefresh( "r_minDisplayRefresh", "0", CVAR_ARCHIVE | CVAR_INTEGER, "" );
48 static idCVar r_maxDisplayRefresh( "r_maxDisplayRefresh", "0", CVAR_ARCHIVE | CVAR_INTEGER, "" );
49 static idCVar r_screen( "r_screen", "-1", CVAR_ARCHIVE | CVAR_INTEGER, "which display to use" );
50
51 static void                             GLW_InitExtensions( void );
52 static bool                             CreateGameWindow( glimpParms_t parms );
53 static unsigned long    Sys_QueryVideoMemory();
54 CGDisplayErr            Sys_CaptureActiveDisplays(void);
55
56 glwstate_t glw_state;
57 static bool isHidden = false;
58
59 @interface NSOpenGLContext (CGLContextAccess)
60 - (CGLContextObj) cglContext;
61 @end
62
63 @implementation NSOpenGLContext (CGLContextAccess)
64 - (CGLContextObj) cglContext;
65 {
66         return _contextAuxiliary;
67 }
68 @end
69
70 /*
71 ============
72 CheckErrors
73 ============
74 */
75 void CheckErrors( void ) {              
76         GLenum   err;
77
78         err = qglGetError();
79         if ( err != GL_NO_ERROR ) {
80                 common->Error( "glGetError: %s\n", qglGetString( err ) );
81         }
82 }
83
84 #if !defined(NDEBUG) && defined(QGL_CHECK_GL_ERRORS)
85
86 unsigned int QGLBeginStarted = 0;
87
88 void QGLErrorBreak(void) { }
89
90 void QGLCheckError( const char *message ) {
91         GLenum        error;
92         static unsigned int errorCount = 0;
93     
94         error = _glGetError();
95         if (error != GL_NO_ERROR) {
96                 if (errorCount == 100) {
97                         common->Printf("100 GL errors printed ... disabling further error reporting.\n");
98                 } else if (errorCount < 100) {
99                         if (errorCount == 0) {
100                                 common->Warning("BREAK ON QGLErrorBreak to stop at the GL errors\n");
101                         }
102                         common->Warning("OpenGL Error(%s): 0x%04x -- %s\n", message, (int)error,  gluErrorString(error));
103                         QGLErrorBreak();
104                 }
105                 errorCount++;
106         }
107 }
108 #endif
109
110 /*
111 ** GLimp_SetMode
112 */
113
114 bool GLimp_SetMode(  glimpParms_t parms ) {
115         if ( !CreateGameWindow( parms ) ) {
116                 common->Printf( "GLimp_SetMode: window could not be created!\n" );
117                 return false;
118         }
119
120         glConfig.vidWidth = parms.width;
121         glConfig.vidHeight = parms.height;
122         glConfig.isFullscreen = parms.fullScreen;
123     
124         // draw something to show that GL is alive      
125         qglClearColor( 0.5, 0.5, 0.7, 0 );
126         qglClear( GL_COLOR_BUFFER_BIT );
127         GLimp_SwapBuffers();
128         
129         qglClearColor( 0.5, 0.5, 0.7, 0 );
130         qglClear( GL_COLOR_BUFFER_BIT );
131         GLimp_SwapBuffers();
132
133         Sys_UnfadeScreen( Sys_DisplayToUse(), NULL );
134     
135         CheckErrors();
136
137         return true;
138 }
139
140 /*
141 =================
142 GetPixelAttributes
143 =================
144 */
145
146 #define ADD_ATTR(x)     \
147         do {                                                               \
148                 if (attributeIndex >= attributeSize) {  \
149                         attributeSize *= 2;     \
150                         pixelAttributes = (NSOpenGLPixelFormatAttribute *)NSZoneRealloc(NULL, pixelAttributes, sizeof(*pixelAttributes) * attributeSize); \
151                 } \
152                 pixelAttributes[attributeIndex] = (NSOpenGLPixelFormatAttribute)x;      \
153                 attributeIndex++;                                               \
154                 if ( verbose ) {                                                                                                \
155                         common->Printf( "Adding pixel attribute: %d (%s)\n", x, #x); \
156                 }                                                                                                                               \
157         } while(0)
158
159 static NSOpenGLPixelFormatAttribute *GetPixelAttributes( unsigned int multisamples ) {
160         NSOpenGLPixelFormatAttribute *pixelAttributes;
161         unsigned int attributeIndex = 0;
162         unsigned int attributeSize = 128;
163         int verbose;
164         unsigned int colorDepth;
165         unsigned int desktopColorDepth;
166         unsigned int depthBits;
167         unsigned int stencilBits;
168         unsigned int buffers;
169     
170         verbose = 0;
171     
172         pixelAttributes = (NSOpenGLPixelFormatAttribute *)NSZoneMalloc(NULL, sizeof(*pixelAttributes) * attributeSize);
173
174         // only greater or equal attributes will be selected
175         ADD_ATTR( NSOpenGLPFAMinimumPolicy );
176         ADD_ATTR( 1 );
177
178         if ( cvarSystem->GetCVarBool( "r_fullscreen" ) ) {
179                 ADD_ATTR(NSOpenGLPFAFullScreen);
180         }
181
182         ADD_ATTR(NSOpenGLPFAScreenMask);
183         ADD_ATTR(CGDisplayIDToOpenGLDisplayMask(Sys_DisplayToUse()));
184         
185         // Require hardware acceleration
186         ADD_ATTR(NSOpenGLPFAAccelerated);
187
188         // Require double-buffer
189         ADD_ATTR(NSOpenGLPFADoubleBuffer);
190
191         // color bits
192         ADD_ATTR(NSOpenGLPFAColorSize);
193         colorDepth = 32;
194         if ( !cvarSystem->GetCVarBool( "r_fullscreen" ) ) {
195                 desktopColorDepth = [[glw_state.desktopMode objectForKey: (id)kCGDisplayBitsPerPixel] intValue];
196                 if ( desktopColorDepth != 32 ) {
197                         common->Warning( "Desktop display colors should be 32 bits for window rendering" );
198                 }
199         }
200         ADD_ATTR(colorDepth);
201
202         // Specify the number of depth bits
203         ADD_ATTR( NSOpenGLPFADepthSize );
204         depthBits = 24;
205         ADD_ATTR( depthBits );
206
207         // Specify the number of stencil bits
208         stencilBits = 8;
209         ADD_ATTR( NSOpenGLPFAStencilSize );
210         ADD_ATTR( stencilBits );
211
212         // Specify destination alpha
213         ADD_ATTR( NSOpenGLPFAAlphaSize );
214         ADD_ATTR( 8 );
215
216         if ( multisamples ) {
217                 buffers = 1;
218                 ADD_ATTR( NSOpenGLPFASampleBuffers );   
219                 ADD_ATTR( buffers );
220                 ADD_ATTR( NSOpenGLPFASamples ); 
221                 ADD_ATTR( multisamples );
222         }
223
224         // Terminate the list
225         ADD_ATTR(0);
226     
227         return pixelAttributes;
228 }
229
230 void Sys_UpdateWindowMouseInputRect(void) {             
231         NSRect           windowRect, screenRect;
232         NSScreen        *screen;
233
234         /*
235
236         // TTimo - I guess glw_state.window is bogus .. getting crappy data out of this
237
238         // It appears we need to flip the coordinate system here.  This means we need
239         // to know the size of the screen.
240         screen = [glw_state.window screen];
241         screenRect = [screen frame];
242         windowRect = [glw_state.window frame];
243         windowRect.origin.y = screenRect.size.height - (windowRect.origin.y + windowRect.size.height);
244
245         Sys_SetMouseInputRect( CGRectMake( windowRect.origin.x, windowRect.origin.y, windowRect.size.width, windowRect.size.height ) );
246         */
247
248         Sys_SetMouseInputRect( CGDisplayBounds( glw_state.display ) );
249 }                                                       
250
251 // This is needed since CGReleaseAllDisplays() restores the gamma on the displays and we want to fade it up rather than just flickering all the displays
252 static void ReleaseAllDisplays() {
253         CGDisplayCount displayIndex;
254
255         common->Printf("Releasing displays\n");
256         for (displayIndex = 0; displayIndex < glw_state.displayCount; displayIndex++) {
257                 CGDisplayRelease(glw_state.originalDisplayGammaTables[displayIndex].display);
258         }
259 }
260
261 /*
262 =================
263 CreateGameWindow
264 =================
265 */
266 static bool CreateGameWindow(  glimpParms_t parms ) {
267         const char                                              *windowed[] = { "Windowed", "Fullscreen" };
268         NSOpenGLPixelFormatAttribute    *pixelAttributes;
269         NSOpenGLPixelFormat                             *pixelFormat;
270         CGDisplayErr                                    err;
271         unsigned int                                    multisamples;
272         const long                                              swap_limit = false;
273         int                                                     nsOpenGLCPSwapLimit = 203;
274             
275         glw_state.display = Sys_DisplayToUse();
276         glw_state.desktopMode = (NSDictionary *)CGDisplayCurrentMode( glw_state.display );
277         if ( !glw_state.desktopMode ) {
278                 common->Error( "Could not get current graphics mode for display 0x%08x\n", glw_state.display );
279         }
280
281         common->Printf(  " %d %d %s\n", parms.width, parms.height, windowed[ parms.fullScreen ] );
282
283         if (parms.fullScreen) {
284         
285                 // We'll set up the screen resolution first in case that effects the list of pixel
286                 // formats that are available (for example, a smaller frame buffer might mean more
287                 // bits for depth/stencil buffers).  Allow stretched video modes if we are in fallback mode.
288                 glw_state.gameMode = Sys_GetMatchingDisplayMode(parms);
289                 if (!glw_state.gameMode) {
290                         common->Printf(  "Unable to find requested display mode.\n");
291                         return false;
292                 }
293
294                 // Fade all screens to black
295                 //        Sys_FadeScreens();
296         
297                 err = Sys_CaptureActiveDisplays();
298                 if ( err != CGDisplayNoErr ) {
299                         CGDisplayRestoreColorSyncSettings();
300                         common->Printf(  " Unable to capture displays err = %d\n", err );
301                         return false;
302                 }
303
304                 err = CGDisplaySwitchToMode(glw_state.display, (CFDictionaryRef)glw_state.gameMode);
305                 if ( err != CGDisplayNoErr ) {
306                         CGDisplayRestoreColorSyncSettings();
307                         ReleaseAllDisplays();
308                         common->Printf(  " Unable to set display mode, err = %d\n", err );
309                         return false;
310                 }
311         } else {
312                 glw_state.gameMode = glw_state.desktopMode;
313         }
314     
315         // Get the GL pixel format
316         pixelFormat = nil;
317         multisamples = cvarSystem->GetCVarInteger( "r_multiSamples" );
318         while ( !pixelFormat ) {
319                 pixelAttributes = GetPixelAttributes( multisamples );
320                 pixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes: pixelAttributes] autorelease];
321                 NSZoneFree(NULL, pixelAttributes);
322                 if ( pixelFormat || multisamples == 0 )
323                         break;
324                 multisamples >>= 1;
325         }
326         cvarSystem->SetCVarInteger( "r_multiSamples", multisamples );                   
327     
328         if (!pixelFormat) {
329                 CGDisplayRestoreColorSyncSettings();
330                 CGDisplaySwitchToMode(glw_state.display, (CFDictionaryRef)glw_state.desktopMode);
331                 ReleaseAllDisplays();
332                 common->Printf(  " No pixel format found\n");
333                 return false;
334         }
335
336         // Create a context with the desired pixel attributes
337         OSX_SetGLContext([[NSOpenGLContext alloc] initWithFormat: pixelFormat shareContext: nil]);
338         if ( !OSX_GetNSGLContext() ) {
339                 CGDisplayRestoreColorSyncSettings();
340                 CGDisplaySwitchToMode(glw_state.display, (CFDictionaryRef)glw_state.desktopMode);
341                 ReleaseAllDisplays();
342                 common->Printf(  "... +[NSOpenGLContext createWithFormat:share:] failed.\n" );
343                 return false;
344         }
345 #ifdef __ppc__
346         long system_version = 0;
347         Gestalt( gestaltSystemVersion, &system_version );
348         if ( parms.width <= 1024 && parms.height <= 768 && system_version <= 0x1045 ) {
349                 [ OSX_GetNSGLContext() setValues: &swap_limit forParameter: (NSOpenGLContextParameter)nsOpenGLCPSwapLimit ];
350         }
351 #endif
352         
353         if ( !parms.fullScreen ) {
354                 NSScreen*                screen;
355                 NSRect           windowRect;
356                 int                              displayIndex;
357                 int                              displayCount;
358                 
359                 displayIndex = r_screen.GetInteger();
360                 displayCount = [[NSScreen screens] count];
361                 if ( displayIndex < 0 || displayIndex >= displayCount ) {
362                         screen = [NSScreen mainScreen];
363                 } else {
364                         screen = [[NSScreen screens] objectAtIndex:displayIndex];
365                 }
366
367                 NSRect r = [screen frame];
368                 windowRect.origin.x =  ((short)r.size.width - parms.width) / 2;
369                 windowRect.origin.y  = ((short)r.size.height - parms.height) / 2;
370                 windowRect.size.width = parms.width;
371                 windowRect.size.height = parms.height;
372         
373                 glw_state.window = [NSWindow alloc];
374                 [glw_state.window initWithContentRect:windowRect styleMask:NSTitledWindowMask backing:NSBackingStoreRetained defer:NO screen:screen];
375                                                            
376                 [glw_state.window setTitle: @GAME_NAME];
377
378                 [glw_state.window orderFront: nil];
379
380                 // Always get mouse moved events (if mouse support is turned off (rare)
381                 // the event system will filter them out.
382                 [glw_state.window setAcceptsMouseMovedEvents: YES];
383         
384                 // Direct the context to draw in this window
385                 [OSX_GetNSGLContext() setView: [glw_state.window contentView]];
386
387                 // Sync input rect with where the window actually is...
388                 Sys_UpdateWindowMouseInputRect();
389         } else {
390                 CGLError err;
391
392                 glw_state.window = NULL;
393         
394                 err = CGLSetFullScreen(OSX_GetCGLContext());
395                 if (err) {
396                         CGDisplayRestoreColorSyncSettings();
397                         CGDisplaySwitchToMode(glw_state.display, (CFDictionaryRef)glw_state.desktopMode);
398                         ReleaseAllDisplays();
399                         common->Printf("CGLSetFullScreen -> %d (%s)\n", err, CGLErrorString(err));
400                         return false;
401                 }
402
403                 Sys_SetMouseInputRect( CGDisplayBounds( glw_state.display ) );
404         }
405
406 #ifndef USE_CGLMACROS
407         // Make this the current context
408         OSX_GLContextSetCurrent();
409 #endif
410
411         // Store off the pixel format attributes that we actually got
412         [pixelFormat getValues: (long *) &glConfig.colorBits forAttribute: NSOpenGLPFAColorSize forVirtualScreen: 0];
413         [pixelFormat getValues: (long *) &glConfig.depthBits forAttribute: NSOpenGLPFADepthSize forVirtualScreen: 0];
414         [pixelFormat getValues: (long *) &glConfig.stencilBits forAttribute: NSOpenGLPFAStencilSize forVirtualScreen: 0];
415
416         glConfig.displayFrequency = [[glw_state.gameMode objectForKey: (id)kCGDisplayRefreshRate] intValue];
417     
418         common->Printf(  "ok\n" );
419
420         return true;
421 }
422
423 // This can be used to temporarily disassociate the GL context from the screen so that CoreGraphics can be used to draw to the screen.
424 void Sys_PauseGL () {
425         if (!glw_state.glPauseCount) {
426                 qglFinish (); // must do this to ensure the queue is complete
427         
428                 // Have to call both to actually deallocate kernel resources and free the NSSurface
429                 CGLClearDrawable(OSX_GetCGLContext());
430                 [OSX_GetNSGLContext() clearDrawable];
431         }
432         glw_state.glPauseCount++;
433 }
434
435 // This can be used to reverse the pausing caused by Sys_PauseGL()
436 void Sys_ResumeGL () {
437         if (glw_state.glPauseCount) {
438                 glw_state.glPauseCount--;
439                 if (!glw_state.glPauseCount) {
440                         if (!glConfig.isFullscreen) {
441                                 [OSX_GetNSGLContext() setView: [glw_state.window contentView]];
442                         } else {
443                                 CGLError err;
444                 
445                                 err = CGLSetFullScreen(OSX_GetCGLContext());
446                                 if (err)
447                                         common->Printf("CGLSetFullScreen -> %d (%s)\n", err, CGLErrorString(err));
448                         }
449                 }
450         }
451 }
452
453 /*
454 ===================
455 GLimp_Init
456
457 Don't return unless OpenGL has been properly initialized
458 ===================
459 */
460
461 #ifdef OMNI_TIMER
462 static void GLImp_Dump_Stamp_List_f(void) {
463         OTStampListDumpToFile(glThreadStampList, "/tmp/gl_stamps");
464 }
465 #endif
466
467 bool GLimp_Init( glimpParms_t parms ) {
468         char *buf;
469
470         common->Printf(  "Initializing OpenGL subsystem\n" );
471         common->Printf(  "  fullscreen: %s\n", cvarSystem->GetCVarBool( "r_fullscreen" ) ? "yes" : "no" );
472
473         Sys_StoreGammaTables();
474     
475         if ( !Sys_QueryVideoMemory() ) {
476                 common->Error(  "Could not initialize OpenGL.  There does not appear to be an OpenGL-supported video card in your system.\n" );
477         }
478     
479         if ( !GLimp_SetMode( parms ) ) {
480                 common->Warning(  "Could not initialize OpenGL\n" );
481                 return false;
482         }
483
484         common->Printf(  "------------------\n" );
485
486         // get our config strings
487         glConfig.vendor_string = (const char *)qglGetString( GL_VENDOR );
488         glConfig.renderer_string = (const char *)qglGetString( GL_RENDERER );
489         glConfig.version_string = (const char *)qglGetString( GL_VERSION );
490         glConfig.extensions_string = (const char *)qglGetString( GL_EXTENSIONS );
491
492         //
493         // chipset specific configuration
494         //
495         buf = (char *)malloc(strlen(glConfig.renderer_string) + 1);
496         strcpy( buf, glConfig.renderer_string );
497
498         //      Cvar_Set( "r_lastValidRenderer", glConfig.renderer_string );
499         free(buf);
500
501         GLW_InitExtensions();
502
503         /*    
504 #ifndef USE_CGLMACROS
505         if (!r_enablerender->integer)
506                 OSX_GLContextClearCurrent();
507 #endif
508         */
509         return true;
510 }
511
512
513 /*
514 ** GLimp_SwapBuffers
515 ** 
516 ** Responsible for doing a swapbuffers and possibly for other stuff
517 ** as yet to be determined.  Probably better not to make this a GLimp
518 ** function and instead do a call to GLimp_SwapBuffers.
519 */
520 void GLimp_SwapBuffers( void ) {
521         if ( r_swapInterval.IsModified() ) {
522                 r_swapInterval.ClearModified();
523         }
524
525 #if !defined(NDEBUG) && defined(QGL_CHECK_GL_ERRORS)
526         QGLCheckError("GLimp_EndFrame");
527 #endif
528
529         if (!glw_state.glPauseCount && !isHidden) {
530                 glw_state.bufferSwapCount++;
531                 [OSX_GetNSGLContext() flushBuffer];
532         }
533
534         /*    
535         // Enable turning off GL at any point for performance testing
536         if (OSX_GLContextIsCurrent() != r_enablerender->integer) {
537                 if (r_enablerender->integer) {
538                         common->Printf("--- Enabling Renderer ---\n");
539                         OSX_GLContextSetCurrent();
540                 } else {
541                         common->Printf("--- Disabling Renderer ---\n");
542                         OSX_GLContextClearCurrent();
543                 }
544         }
545         */
546 }
547
548 /*
549 ** GLimp_Shutdown
550 **
551 ** This routine does all OS specific shutdown procedures for the OpenGL
552 ** subsystem.  Under OpenGL this means NULLing out the current DC and
553 ** HGLRC, deleting the rendering context, and releasing the DC acquired
554 ** for the window.  The state structure is also nulled out.
555 **
556 */
557
558 static void _GLimp_RestoreOriginalVideoSettings() {
559         CGDisplayErr err;
560     
561         // CGDisplayCurrentMode lies because we've captured the display and thus we won't
562         // get any notifications about what the current display mode really is.  For now,
563         // we just always force it back to what mode we remember the desktop being in.
564         if (glConfig.isFullscreen) {
565                 err = CGDisplaySwitchToMode(glw_state.display, (CFDictionaryRef)glw_state.desktopMode);
566                 if ( err != CGDisplayNoErr )
567                         common->Printf(  " Unable to restore display mode!\n" );
568
569                 ReleaseAllDisplays();
570         }
571 }
572
573 void GLimp_Shutdown( void ) {
574         CGDisplayCount displayIndex;
575
576         common->Printf("----- Shutting down GL -----\n");
577
578         Sys_FadeScreen(Sys_DisplayToUse());
579     
580         if (OSX_GetNSGLContext()) {
581 #ifndef USE_CGLMACROS
582                 OSX_GLContextClearCurrent();
583 #endif
584                 // Have to call both to actually deallocate kernel resources and free the NSSurface
585                 CGLClearDrawable(OSX_GetCGLContext());
586                 [OSX_GetNSGLContext() clearDrawable];
587         
588                 [OSX_GetNSGLContext() release];
589                 OSX_SetGLContext((id)nil);
590         }
591
592         _GLimp_RestoreOriginalVideoSettings();
593     
594         Sys_UnfadeScreens();
595
596         // Restore the original gamma if needed.
597         //    if (glConfig.deviceSupportsGamma) {
598         //        common->Printf("Restoring ColorSync settings\n");
599         //        CGDisplayRestoreColorSyncSettings();
600         //    }
601
602         if (glw_state.window) {
603                 [glw_state.window release];
604                 glw_state.window = nil;
605         }
606
607         if (glw_state.log_fp) {
608                 fclose(glw_state.log_fp);
609                 glw_state.log_fp = 0;
610         }
611
612         for (displayIndex = 0; displayIndex < glw_state.displayCount; displayIndex++) {
613                 free(glw_state.originalDisplayGammaTables[displayIndex].red);
614                 free(glw_state.originalDisplayGammaTables[displayIndex].blue);
615                 free(glw_state.originalDisplayGammaTables[displayIndex].green);
616         }
617         free(glw_state.originalDisplayGammaTables);
618         if (glw_state.tempTable.red) {
619                 free(glw_state.tempTable.red);
620                 free(glw_state.tempTable.blue);
621                 free(glw_state.tempTable.green);
622         }
623         if (glw_state.inGameTable.red) {
624                 free(glw_state.inGameTable.red);
625                 free(glw_state.inGameTable.blue);
626                 free(glw_state.inGameTable.green);
627         }
628     
629         memset(&glConfig, 0, sizeof(glConfig));
630         //    memset(&glState, 0, sizeof(glState));
631         memset(&glw_state, 0, sizeof(glw_state));
632
633         common->Printf("----- Done shutting down GL -----\n");
634 }
635
636 /*
637 ===============
638 GLimp_LogComment
639 ===============
640 */
641 void    GLimp_LogComment( char *comment ) { }
642
643 /*
644 ===============
645 GLimp_SetGamma
646 ===============
647 */
648 void GLimp_SetGamma(unsigned short red[256],
649                     unsigned short green[256],
650                     unsigned short blue[256]) {
651         CGGammaValue redGamma[256], greenGamma[256], blueGamma[256];
652         CGTableCount i;
653         CGDisplayErr err;
654         
655         for (i = 0; i < 256; i++) {
656                 redGamma[i]   = red[i]   / 65535.0;
657                 greenGamma[i] = green[i] / 65535.0;
658                 blueGamma[i]  = blue[i]  / 65535.0;
659         }
660     
661         err = CGSetDisplayTransferByTable(glw_state.display, 256, redGamma, greenGamma, blueGamma);
662         if (err != CGDisplayNoErr) {
663                 common->Printf("GLimp_SetGamma: CGSetDisplayTransferByByteTable returned %d.\n", err);
664         }
665     
666         // Store the gamma table that we ended up using so we can reapply it later when unhiding or to work around the bug where if you leave the game sitting and the monitor sleeps, when it wakes, the gamma isn't reset.
667         glw_state.inGameTable.display = glw_state.display;
668         Sys_GetGammaTable(&glw_state.inGameTable);
669 }
670
671 /*****************************************************************************/
672
673 #pragma mark -
674 #pragma mark Â¥ ATI_fragment_shader
675
676 static GLuint sGeneratingProgram = 0;
677 static int sCurrentPass;
678 static char sConstString[4096];
679 static char sPassString[2][4096];
680 static int sOpUsed;
681 static int sConstUsed;
682 static int sConst[8];
683 static GLfloat sConstVal[8][4];
684
685 static void _endPass (void) {
686         if (!sOpUsed) return;
687         sOpUsed = 0;
688         sCurrentPass ++;
689 }
690
691 GLuint glGenFragmentShadersATI (GLuint ID) {
692         qglGenProgramsARB(1, &ID);
693         return ID;
694 }
695
696 void glBindFragmentShaderATI (GLuint ID) {
697         qglBindProgramARB(GL_TEXT_FRAGMENT_SHADER_ATI, ID);
698 }
699
700 void glDeleteFragmentShaderATI (GLuint ID) {
701 //      qglDeleteProgramsARB(1, &ID);
702 }
703
704 void glBeginFragmentShaderATI (void) {
705         int i;
706
707         sConstString[0] = 0;
708         for (i = 0; i < 8; i ++)
709                 sConst[i] = 0;
710         
711         sOpUsed = 0;
712         sPassString[0][0] = 0;
713         sPassString[1][0] = 0;
714         
715         sCurrentPass = 0;
716         sGeneratingProgram = 1;
717 }
718
719 void glEndFragmentShaderATI (void) {
720         GLint errPos;
721         int i;
722         char fragString[4096];
723         
724         sGeneratingProgram = 0;
725
726         // header
727         strcpy(fragString, "!!ATIfs1.0\n");
728         
729         // constants
730         if (sConstString[0] || sConstUsed) {
731                 strcat (fragString, "StartConstants;\n");
732                 if (sConstUsed) {
733                         for (i = 0; i < 8; i ++) {
734                                 if (sConst[i] == 1) {
735                                         char str[128];
736                                         sprintf (str, "    CONSTANT c%d = program.env[%d];\n", i, i);
737                                         strcat (fragString, str);
738                                 }
739                         }
740                 }
741                 if (sConstString[0]) {
742                         strcat (fragString, sConstString);
743                 }
744                 strcat (fragString, "EndConstants;\n\n");
745         }
746
747         if (sCurrentPass == 0) {
748                 strcat(fragString, "StartOutputPass;\n");
749                 strcat(fragString, sPassString[0]);
750                 strcat(fragString, "EndPass;\n");
751         } else {
752                 strcat(fragString, "StartPrelimPass;\n");
753                 strcat(fragString, sPassString[0]);
754                 strcat(fragString, "EndPass;\n\n");
755
756                 strcat(fragString, "StartOutputPass;\n");
757                 strcat(fragString, sPassString[1]);
758                 strcat(fragString, "EndPass;\n");
759         }
760
761         qglProgramStringARB(GL_TEXT_FRAGMENT_SHADER_ATI, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragString), fragString);
762         qglGetIntegerv( GL_PROGRAM_ERROR_POSITION_ARB, &errPos );
763         if(errPos != -1) {
764                 const GLubyte *errString = glGetString(GL_PROGRAM_ERROR_STRING_ARB);
765                 common->Warning("WARNING: glError at %d:%s when compiling atiFragmentShader %s", errPos, errString, fragString);
766         }
767 }
768
769
770 void glSetFragmentShaderConstantATI (GLuint num, const GLfloat *val) {
771         int constNum = num-GL_CON_0_ATI;
772         if (sGeneratingProgram) {
773                 char str[128];
774                 sprintf (str, "    CONSTANT c%d = { %f, %f, %f, %f };\n", constNum, val[0], val[1], val[2], val[3]);
775                 strcat (sConstString, str);
776                 sConst[constNum] = 2;
777         }
778         else {
779                 // According to Duane, frequent setting of fragment shader constants, even if they contain
780                 // the same value, will cause a performance hit.
781                 // According to Chris Bentley at ATI, this performance hit appears if you are using
782                 // many different fragment shaders in each scene.
783                 // So, we cache those values and only set the constants if they are different.
784                 if (memcmp (val, sConstVal[constNum], sizeof(GLfloat)*8*4) != 0)
785                 {
786                         qglProgramEnvParameter4fvARB (GL_TEXT_FRAGMENT_SHADER_ATI, num-GL_CON_0_ATI, val);
787                         memcpy (sConstVal[constNum], val, sizeof(GLfloat)*8*4);
788                 }
789         }
790 }
791
792 char *makeArgStr(GLuint arg) {
793         // Since we return "str", it needs to be static to ensure that it retains
794         // its value outside this routine. 
795         static char str[128];
796         
797         strcpy (str, "");
798         
799         if ( arg >= GL_REG_0_ATI && arg <= GL_REG_5_ATI ) {
800                 sprintf(str, "r%d", arg - GL_REG_0_ATI);
801         } else if(arg >= GL_CON_0_ATI && arg <= GL_CON_7_ATI) {
802                 if(!sConst[arg - GL_CON_0_ATI]) {
803                         sConstUsed = 1;
804                         sConst[arg - GL_CON_0_ATI] = 1;
805                 }
806                 sprintf(str, "c%d", arg - GL_CON_0_ATI);
807         } else if( arg >= GL_TEXTURE0_ARB && arg <= GL_TEXTURE31_ARB ) {
808                 sprintf(str, "t%d", arg - GL_TEXTURE0_ARB);
809         } else if( arg == GL_PRIMARY_COLOR_ARB ) {
810                 strcpy(str, "color0");  
811         } else if(arg == GL_SECONDARY_INTERPOLATOR_ATI) {
812                 strcpy(str, "color1");
813         } else if (arg == GL_ZERO) {
814                 strcpy(str, "0");
815         } else if (arg == GL_ONE) {
816                 strcpy(str, "1");
817         } else {
818                 common->Warning("makeArgStr: bad arg value\n");
819         }
820         return str;
821 }
822
823 void glPassTexCoordATI (GLuint dst, GLuint coord, GLenum swizzle) {
824         char str[128] = "\0";
825         _endPass();
826
827         switch(swizzle) {
828                 case GL_SWIZZLE_STR_ATI:
829                         sprintf(str, "    PassTexCoord r%d, %s.str;\n", dst - GL_REG_0_ATI, makeArgStr(coord));
830                         break;
831                 case GL_SWIZZLE_STQ_ATI:
832                         sprintf(str, "    PassTexCoord r%d, %s.stq;\n", dst - GL_REG_0_ATI, makeArgStr(coord));
833                         break;
834                 case GL_SWIZZLE_STR_DR_ATI:
835                         sprintf(str, "    PassTexCoord r%d, %s.str_dr;\n", dst - GL_REG_0_ATI, makeArgStr(coord));
836                         break;
837                 case GL_SWIZZLE_STQ_DQ_ATI:
838                         sprintf(str, "    PassTexCoord r%d, %s.stq_dq;\n", dst - GL_REG_0_ATI, makeArgStr(coord));
839                         break;
840                 default:
841                         common->Warning("glPassTexCoordATI invalid swizzle;");
842                         break;
843         }
844         strcat(sPassString[sCurrentPass], str);
845 }
846
847 void glSampleMapATI (GLuint dst, GLuint interp, GLenum swizzle) {
848         char str[128] = "\0";
849         _endPass();
850
851         switch(swizzle) {
852                 case GL_SWIZZLE_STR_ATI:
853                         sprintf(str, "    SampleMap r%d, %s.str;\n", dst - GL_REG_0_ATI, makeArgStr(interp));
854                         break;
855                 case GL_SWIZZLE_STQ_ATI:
856                         sprintf(str, "    SampleMap r%d, %s.stq;\n", dst - GL_REG_0_ATI, makeArgStr(interp));
857                         break;
858                 case GL_SWIZZLE_STR_DR_ATI:
859                         sprintf(str, "    SampleMap r%d, %s.str_dr;\n", dst - GL_REG_0_ATI, makeArgStr(interp));
860                         break;
861                 case GL_SWIZZLE_STQ_DQ_ATI:
862                         sprintf(str, "    SampleMap r%d, %s.stq_dq;\n", dst - GL_REG_0_ATI, makeArgStr(interp));
863                         break;
864                 default:
865                         common->Warning("glSampleMapATI invalid swizzle;");
866                         break;
867         }
868         strcat(sPassString[sCurrentPass], str);
869 }
870
871 char *makeMaskStr(GLuint mask) {
872         // Since we return "str", it needs to be static to ensure that it retains
873         // its value outside this routine. 
874         static char str[128];
875         
876         strcpy (str, "");
877         
878         switch (mask) {
879                 case GL_NONE:
880                         str[0] = '\0';
881                         break;
882                 case GL_RGBA:
883                         strcpy(str, ".rgba");
884                         break;
885                 case GL_RGB:
886                         strcpy(str, ".rgb");
887                         break;
888                 case GL_RED:
889                         strcpy(str, ".r");
890                         break;
891                 case GL_GREEN:
892                         strcpy(str, ".g");
893                         break;
894                 case GL_BLUE:
895                         strcpy(str, ".b");
896                         break;
897                 case GL_ALPHA:
898                         strcpy(str, ".a");
899                         break;
900                 default:
901                         strcpy(str, ".");
902                         if( mask & GL_RED_BIT_ATI )
903                                 strcat(str, "r");
904                         if( mask & GL_GREEN_BIT_ATI )
905                                 strcat(str, "g");
906                         if( mask & GL_BLUE_BIT_ATI )
907                                 strcat(str, "b");
908                         break;
909         }
910                 
911         return str;
912 }
913
914 char *makeDstModStr(GLuint mod) {
915         // Since we return "str", it needs to be static to ensure that it retains
916         // its value outside this routine. 
917         static char str[128];
918         
919         strcpy (str, "");
920         
921         if( mod == GL_NONE) {
922                 str[0] = '\0';
923                 return str;
924         }
925         if( mod & GL_2X_BIT_ATI) {
926                 strcat(str, ".2x");
927         }
928
929         if( mod & GL_4X_BIT_ATI) {
930                 strcat(str, ".4x");
931         }
932
933         if( mod & GL_8X_BIT_ATI) {
934                 strcat(str, ".8x");
935         }
936
937         if( mod & GL_SATURATE_BIT_ATI) {
938                 strcat(str, ".sat");
939         }
940
941         if( mod & GL_HALF_BIT_ATI) {
942                 strcat(str, ".half");
943         }
944         
945         if( mod & GL_QUARTER_BIT_ATI) {
946                 strcat(str, ".quarter");
947         }
948
949         if( mod & GL_EIGHTH_BIT_ATI) {
950                 strcat(str, ".eighth");
951         }
952
953         return str;
954 }       
955
956 char *makeArgModStr(GLuint mod) {
957         // Since we return "str", it needs to be static to ensure that it retains
958         // its value outside this routine. 
959         static char str[128];
960         
961         strcpy (str, "");
962         
963         if( mod == GL_NONE) {
964                 str[0] = '\0';
965                 return str;
966         }
967         if( mod & GL_NEGATE_BIT_ATI) {
968                 strcat(str, ".neg");
969         }
970
971         if( mod & GL_2X_BIT_ATI) {
972                 strcat(str, ".2x");
973         }
974
975         if( mod & GL_BIAS_BIT_ATI) {
976                 strcat(str, ".bias");
977         }
978                 
979         if( mod & GL_COMP_BIT_ATI) {
980                 strcat(str, ".comp");
981         }
982                 
983         return str;
984 }
985
986 void glColorFragmentOp1ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod) {
987         char str[128] = "\0";
988         
989         sOpUsed = 1;
990         
991         switch(op) {
992                 // Unary operators
993                 case GL_MOV_ATI:
994                         sprintf(str, "    MOV r%d", dst - GL_REG_0_ATI);
995                         break;
996                 default:
997                         common->Warning("glColorFragmentOp1ATI invalid op;\n");
998                         break;
999         }
1000         if(dstMask != GL_NONE)  {
1001                 strcat(str, makeMaskStr(dstMask));
1002         }
1003         else {
1004                 strcat(str, ".rgb" );
1005         }
1006         
1007         if(dstMod != GL_NONE) {
1008                 strcat(str, makeDstModStr(dstMod));
1009         }
1010         strcat(str, ", ");
1011         
1012         strcat(str, makeArgStr(arg1));
1013         if(arg1Rep != GL_NONE)  {
1014                 strcat(str, makeMaskStr(arg1Rep));
1015         }
1016         if(arg1Mod != GL_NONE) {
1017                 strcat(str, makeArgModStr(arg1Mod));
1018         }
1019         strcat(str, ";\n");
1020         
1021         strcat(sPassString[sCurrentPass], str);
1022 }
1023
1024 void glColorFragmentOp2ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod) {
1025         char str[128] = "\0";
1026         
1027         if (!sOpUsed)
1028                 sprintf(str,"\n");
1029         sOpUsed = 1;
1030                 
1031         switch(op) {
1032                 // Unary operators - fall back to Op1 routine.
1033                 case GL_MOV_ATI:
1034                         glColorFragmentOp1ATI(op, dst, dstMask, dstMod, arg1, arg1Rep, arg1Mod);
1035                         return;
1036
1037                 // Binary operators
1038                 case GL_ADD_ATI:
1039                         sprintf(str, "    ADD r%d", dst - GL_REG_0_ATI);
1040                         break;
1041                 case GL_MUL_ATI:
1042                         sprintf(str, "    MUL r%d", dst - GL_REG_0_ATI);
1043                         break;
1044                 case GL_SUB_ATI:
1045                         sprintf(str, "    SUB r%d", dst - GL_REG_0_ATI);
1046                         break;
1047                 case GL_DOT3_ATI:
1048                         sprintf(str, "    DOT3 r%d", dst - GL_REG_0_ATI);
1049                         break;
1050                 case GL_DOT4_ATI:
1051                         sprintf(str, "    DOT4 r%d", dst - GL_REG_0_ATI);
1052                         break;
1053                 default:
1054                         common->Warning("glColorFragmentOp2ATI invalid op;");
1055                         break;
1056         }
1057         if(dstMask != GL_NONE)  {
1058                 strcat(str, makeMaskStr(dstMask));
1059         }
1060         else {
1061                 strcat(str, ".rgb" );
1062         }
1063         if(dstMod != GL_NONE) {
1064                 strcat(str, makeDstModStr(dstMod));
1065         }
1066         strcat(str, ", ");
1067         
1068         strcat(str, makeArgStr(arg1));
1069 //      if(arg1Rep != GL_NONE) 
1070                 strcat(str, makeMaskStr(arg1Rep));
1071         if(arg1Mod != GL_NONE) {
1072                 strcat(str, makeArgModStr(arg1Mod));
1073         }
1074         strcat(str, ", ");
1075         
1076         strcat(str, makeArgStr(arg2));
1077 //      if(arg2Rep != GL_NONE) 
1078                 strcat(str, makeMaskStr(arg2Rep));
1079         if(arg2Mod != GL_NONE) {
1080                 strcat(str, makeArgModStr(arg2Mod));                    
1081         }
1082         strcat(str, ";\n");
1083         
1084         strcat(sPassString[sCurrentPass], str);
1085 }
1086
1087 void glColorFragmentOp3ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod) {
1088         char str[128] = "\0";
1089         
1090         sOpUsed = 1;
1091         
1092         switch(op) {
1093                 // Unary operators - fall back to Op1 routine.
1094                 case GL_MOV_ATI:
1095                         glColorFragmentOp1ATI(op, dst, dstMask, dstMod, arg1, arg1Rep, arg1Mod);
1096                         return;
1097
1098                 // Binary operators - fall back to Op2 routine.
1099                 case GL_ADD_ATI:
1100                 case GL_MUL_ATI:
1101                 case GL_SUB_ATI:
1102                 case GL_DOT3_ATI:
1103                 case GL_DOT4_ATI:
1104                         glColorFragmentOp2ATI(op, dst, dstMask, dstMod, arg1, arg1Rep, arg1Mod, arg2, arg2Rep, arg2Mod);
1105                         break;
1106
1107                 // Ternary operators
1108                 case GL_MAD_ATI:
1109                         sprintf(str, "    MAD r%d", dst - GL_REG_0_ATI);
1110                         break;
1111                 case GL_LERP_ATI:
1112                         sprintf(str, "    LERP r%d", dst - GL_REG_0_ATI);
1113                         break;
1114                 case GL_CND_ATI:
1115                         sprintf(str, "    CND r%d", dst - GL_REG_0_ATI);
1116                         break;
1117                 case GL_CND0_ATI:
1118                         sprintf(str, "    CND0 r%d", dst - GL_REG_0_ATI);
1119                         break;
1120                 case GL_DOT2_ADD_ATI:
1121                         sprintf(str, "    DOT2ADD r%d", dst - GL_REG_0_ATI);
1122                         break;
1123                 default:
1124                         common->Warning("glColorFragmentOp3ATI invalid op;");
1125                         break;
1126         }
1127
1128         if(dstMask != GL_NONE)  {
1129                 strcat(str, makeMaskStr(dstMask));
1130         }
1131         else {
1132                 strcat(str, ".rgb" );
1133         }
1134         if(dstMod != GL_NONE) {
1135                 strcat(str, makeDstModStr(dstMod));
1136         }
1137         strcat(str, ", ");
1138         
1139         strcat(str, makeArgStr(arg1));
1140         if(arg1Rep != GL_NONE)  {
1141                 strcat(str, makeMaskStr(arg1Rep));
1142         }
1143         if(arg1Mod != GL_NONE) {
1144                 strcat(str, makeArgModStr(arg1Mod));
1145         }
1146         strcat(str, ", ");
1147         
1148         strcat(str, makeArgStr(arg2));
1149         if(arg2Rep != GL_NONE)  {
1150                 strcat(str, makeMaskStr(arg2Rep));
1151         }
1152         if(arg2Mod != GL_NONE) {
1153                 strcat(str, makeArgModStr(arg2Mod));
1154         }
1155         strcat(str, ", ");
1156                 
1157         strcat(str, makeArgStr(arg3));
1158         if(arg3Rep != GL_NONE)  {
1159                 strcat(str, makeMaskStr(arg3Rep));
1160         }
1161         if(arg3Mod != GL_NONE) {
1162                 strcat(str, makeArgModStr(arg3Mod));
1163         }
1164         strcat(str, ";\n");
1165         
1166         strcat(sPassString[sCurrentPass], str);
1167 }
1168
1169 void glAlphaFragmentOp1ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod) {
1170         glColorFragmentOp1ATI ( op, dst, GL_ALPHA, dstMod, arg1, arg1Rep, arg1Mod);
1171 }
1172
1173 void glAlphaFragmentOp2ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod) {
1174         glColorFragmentOp2ATI ( op, dst, GL_ALPHA, dstMod, arg1, arg1Rep, arg1Mod, arg2, arg2Rep, arg2Mod);
1175 }
1176
1177 void glAlphaFragmentOp3ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod) {
1178         glColorFragmentOp3ATI ( op, dst, GL_ALPHA, dstMod, arg1, arg1Rep, arg1Mod, arg2, arg2Rep, arg2Mod, arg3, arg3Rep, arg3Mod);
1179 }
1180 #pragma mark -
1181
1182 GLExtension_t GLimp_ExtensionPointer(const char *name) {
1183         NSSymbol symbol;
1184         char *symbolName;
1185
1186         // special case for ATI_fragment_shader calls to map to ATI_text_fragment_shader routines
1187         if (!strcmp(name, "glGenFragmentShadersATI")) {
1188                 return (GLExtension_t)glGenFragmentShadersATI;
1189         }
1190         if (!strcmp(name, "glBindFragmentShaderATI")) {
1191                 return (GLExtension_t)glBindFragmentShaderATI;
1192         }
1193         if (!strcmp(name, "glDeleteFragmentShaderATI")) {
1194                 return (GLExtension_t)glDeleteFragmentShaderATI;
1195         }
1196         if (!strcmp(name, "glBeginFragmentShaderATI")) {
1197                 return (GLExtension_t)glBeginFragmentShaderATI;
1198         }
1199         if (!strcmp(name, "glEndFragmentShaderATI")) {
1200                 return (GLExtension_t)glEndFragmentShaderATI;
1201         }
1202         if (!strcmp(name, "glPassTexCoordATI")) {
1203                 return (GLExtension_t)glPassTexCoordATI;
1204         }
1205         if (!strcmp(name, "glSampleMapATI")) {
1206                 return (GLExtension_t)glSampleMapATI;
1207         }
1208         if (!strcmp(name, "glColorFragmentOp1ATI")) {
1209                 return (GLExtension_t)glColorFragmentOp1ATI;
1210         }
1211         if (!strcmp(name, "glColorFragmentOp2ATI")) {
1212                 return (GLExtension_t)glColorFragmentOp2ATI;
1213         }
1214         if (!strcmp(name, "glColorFragmentOp3ATI")) {
1215                 return (GLExtension_t)glColorFragmentOp3ATI;
1216         }
1217         if (!strcmp(name, "glAlphaFragmentOp1ATI")) {
1218                 return (GLExtension_t)glAlphaFragmentOp1ATI;
1219         }
1220         if (!strcmp(name, "glAlphaFragmentOp2ATI")) {
1221                 return (GLExtension_t)glAlphaFragmentOp2ATI;
1222         }
1223         if (!strcmp(name, "glAlphaFragmentOp3ATI")) {
1224                 return (GLExtension_t)glAlphaFragmentOp3ATI;
1225         }
1226         if (!strcmp(name, "glSetFragmentShaderConstantATI")) {
1227                 return (GLExtension_t)glSetFragmentShaderConstantATI;
1228         }
1229
1230         // Prepend a '_' for the Unix C symbol mangling convention
1231         symbolName = (char *)alloca(strlen(name) + 2);
1232         strcpy(symbolName + 1, name);
1233         symbolName[0] = '_';
1234
1235         if ( !NSIsSymbolNameDefined( symbolName ) ) {
1236                 return NULL;
1237         }
1238
1239         symbol = NSLookupAndBindSymbol(symbolName);
1240         if ( !symbol ) {
1241                 // shouldn't happen ...
1242                 return NULL;
1243         }
1244
1245         return (GLExtension_t)(NSAddressOfSymbol(symbol));
1246 }
1247
1248 void * wglGetProcAddress(const char *name) {
1249         return (void *)GLimp_ExtensionPointer( name );
1250 }
1251
1252
1253 /*
1254 ** GLW_InitExtensions
1255 */
1256 void GLW_InitExtensions( void ) { }
1257
1258 #define MAX_RENDERER_INFO_COUNT 128
1259
1260 // Returns zero if there are no hardware renderers.  Otherwise, returns the max memory across all renderers (on the presumption that the screen that we'll use has the most memory).
1261 unsigned long Sys_QueryVideoMemory() {
1262         CGLError err;
1263         CGLRendererInfoObj rendererInfo, rendererInfos[MAX_RENDERER_INFO_COUNT];
1264         long rendererInfoIndex, rendererInfoCount = MAX_RENDERER_INFO_COUNT;
1265         long rendererIndex, rendererCount;
1266         long maxVRAM = 0, vram = 0;
1267         long accelerated;
1268         long rendererID;
1269         long totalRenderers = 0;
1270     
1271         err = CGLQueryRendererInfo(CGDisplayIDToOpenGLDisplayMask(Sys_DisplayToUse()), rendererInfos, &rendererInfoCount);
1272         if (err) {
1273                 common->Printf("CGLQueryRendererInfo -> %d\n", err);
1274                 return vram;
1275         }
1276     
1277         //common->Printf("rendererInfoCount = %d\n", rendererInfoCount);
1278         for (rendererInfoIndex = 0; rendererInfoIndex < rendererInfoCount && totalRenderers < rendererInfoCount; rendererInfoIndex++) {
1279                 rendererInfo = rendererInfos[rendererInfoIndex];
1280                 //common->Printf("rendererInfo: 0x%08x\n", rendererInfo);
1281         
1282
1283                 err = CGLDescribeRenderer(rendererInfo, 0, kCGLRPRendererCount, &rendererCount);
1284                 if (err) {
1285                         common->Printf("CGLDescribeRenderer(kCGLRPRendererID) -> %d\n", err);
1286                         continue;
1287                 }
1288                 //common->Printf("  rendererCount: %d\n", rendererCount);
1289
1290                 for (rendererIndex = 0; rendererIndex < rendererCount; rendererIndex++) {
1291                         totalRenderers++;
1292                         //common->Printf("  rendererIndex: %d\n", rendererIndex);
1293             
1294                         rendererID = 0xffffffff;
1295                         err = CGLDescribeRenderer(rendererInfo, rendererIndex, kCGLRPRendererID, &rendererID);
1296                         if (err) {
1297                                 common->Printf("CGLDescribeRenderer(kCGLRPRendererID) -> %d\n", err);
1298                                 continue;
1299                         }
1300                         //common->Printf("    rendererID: 0x%08x\n", rendererID);
1301             
1302                         accelerated = 0;
1303                         err = CGLDescribeRenderer(rendererInfo, rendererIndex, kCGLRPAccelerated, &accelerated);
1304                         if (err) {
1305                                 common->Printf("CGLDescribeRenderer(kCGLRPAccelerated) -> %d\n", err);
1306                                 continue;
1307                         }
1308                         //common->Printf("    accelerated: %d\n", accelerated);
1309                         if (!accelerated)
1310                                 continue;
1311             
1312                         vram = 0;
1313                         err = CGLDescribeRenderer(rendererInfo, rendererIndex, kCGLRPVideoMemory, &vram);
1314                         if (err) {
1315                                 common->Printf("CGLDescribeRenderer -> %d\n", err);
1316                                 continue;
1317                         }
1318                         //common->Printf("    vram: 0x%08x\n", vram);
1319             
1320                         // presumably we'll be running on the best card, so we'll take the max of the vrams
1321                         if (vram > maxVRAM)
1322                                 maxVRAM = vram;
1323                 }
1324         
1325 #if 0
1326                 err = CGLDestroyRendererInfo(rendererInfo);
1327                 if (err) {
1328                         common->Printf("CGLDestroyRendererInfo -> %d\n", err);
1329                 }
1330 #endif
1331         }
1332
1333         return maxVRAM;
1334 }
1335
1336
1337 // We will set the Sys_IsHidden global to cause input to be handle differently (we'll just let NSApp handle events in this case).  We also will unbind the GL context and restore the video mode.
1338 bool Sys_Hide() {
1339         if ( isHidden ) {
1340                 // Eh?
1341                 return false;
1342         }
1343     
1344         if ( !r_fullscreen.GetBool() ) {
1345                 // We only support hiding in fullscreen mode right now
1346                 return false;
1347         }
1348     
1349         isHidden = true;
1350
1351         // Don't need to store the current gamma since we always keep it around in glw_state.inGameTable.
1352
1353         Sys_FadeScreen(Sys_DisplayToUse());
1354
1355         // Disassociate the GL context from the screen
1356         // Have to call both to actually deallocate kernel resources and free the NSSurface
1357         CGLClearDrawable(OSX_GetCGLContext());
1358         [OSX_GetNSGLContext() clearDrawable];
1359     
1360         // Restore the original video mode
1361         _GLimp_RestoreOriginalVideoSettings();
1362
1363         // Restore the original gamma if needed.
1364         //    if (glConfig.deviceSupportsGamma) {
1365         //        CGDisplayRestoreColorSyncSettings();
1366         //    }
1367
1368         // Release the screen(s)
1369         ReleaseAllDisplays();
1370     
1371         Sys_UnfadeScreens();
1372     
1373         // Shut down the input system so the mouse and keyboard settings are restore to normal
1374         Sys_ShutdownInput();
1375     
1376         // Hide the application so that when the user clicks on our app icon, we'll get an unhide notification
1377         [NSApp hide: nil];
1378     
1379         return true;
1380 }
1381
1382 CGDisplayErr Sys_CaptureActiveDisplays(void) {
1383         CGDisplayErr err;
1384         CGDisplayCount displayIndex;
1385         for (displayIndex = 0; displayIndex < glw_state.displayCount; displayIndex++) {
1386                 const glwgamma_t *table;
1387                 table = &glw_state.originalDisplayGammaTables[displayIndex];
1388                 err = CGDisplayCapture(table->display);
1389                 if (err != CGDisplayNoErr)
1390             return err;
1391         }
1392         return CGDisplayNoErr;
1393 }
1394
1395 bool Sys_Unhide() {
1396         CGDisplayErr err;
1397         CGLError glErr;
1398     
1399         if ( !isHidden) {
1400                 // Eh?
1401                 return false;
1402         }
1403         
1404         Sys_FadeScreens();
1405
1406         // Capture the screen(s)
1407         err = Sys_CaptureActiveDisplays();
1408         if (err != CGDisplayNoErr) {
1409                 Sys_UnfadeScreens();
1410                 common->Printf(  "Unhide failed -- cannot capture the display again.\n" );
1411                 return false;
1412         }
1413     
1414         // Restore the game mode
1415         err = CGDisplaySwitchToMode(glw_state.display, (CFDictionaryRef)glw_state.gameMode);
1416         if ( err != CGDisplayNoErr ) {
1417                 ReleaseAllDisplays();
1418                 Sys_UnfadeScreens();
1419                 common->Printf(  "Unhide failed -- Unable to set display mode\n" );
1420                 return false;
1421         }
1422
1423         // Reassociate the GL context and the screen
1424         glErr = CGLSetFullScreen(OSX_GetCGLContext());
1425         if (glErr) {
1426                 ReleaseAllDisplays();
1427                 Sys_UnfadeScreens();
1428                 common->Printf(  "Unhide failed: CGLSetFullScreen -> %d (%s)\n", err, CGLErrorString(glErr));
1429                 return false;
1430         }
1431
1432         // Restore the current context
1433         [OSX_GetNSGLContext() makeCurrentContext];
1434     
1435         // Restore the gamma that the game had set
1436         Sys_UnfadeScreen(Sys_DisplayToUse(), &glw_state.inGameTable);
1437     
1438         // Restore the input system (last so if something goes wrong we don't eat the mouse)
1439         Sys_InitInput();
1440     
1441         isHidden = false;
1442         return true;
1443 }
1444
1445 bool GLimp_SpawnRenderThread( void (*function)( void ) ) {
1446         return false;
1447 }
1448
1449 void *GLimp_RendererSleep(void) {
1450         return NULL;
1451 }
1452
1453 void GLimp_FrontEndSleep(void) { }
1454
1455 void GLimp_WakeRenderer( void *data ) { }
1456
1457 void *GLimp_BackEndSleep( void ) {
1458         return NULL;
1459 }
1460
1461 void GLimp_WakeBackEnd( void *data ) {
1462 }
1463
1464 // enable / disable context is just for the r_skipRenderContext debug option
1465 void GLimp_DeactivateContext( void ) {
1466         [NSOpenGLContext clearCurrentContext];
1467 }
1468
1469 void GLimp_ActivateContext( void ) {
1470         [OSX_GetNSGLContext() makeCurrentContext];
1471 }
1472
1473 void GLimp_EnableLogging(bool stat) { }
1474
1475 NSDictionary *Sys_GetMatchingDisplayMode( glimpParms_t parms ) {
1476         NSArray *displayModes;
1477         NSDictionary *mode;
1478         unsigned int modeIndex, modeCount, bestModeIndex;
1479         int verbose;
1480         //      cvar_t *cMinFreq, *cMaxFreq;
1481         int minFreq, maxFreq;
1482         unsigned int colorDepth;
1483     
1484         verbose = 0;
1485
1486         colorDepth = 32;
1487
1488         minFreq = r_minDisplayRefresh.GetInteger();
1489         maxFreq = r_maxDisplayRefresh.GetInteger();
1490         if ( minFreq > maxFreq ) {
1491                 common->Error( "r_minDisplayRefresh must be less than or equal to r_maxDisplayRefresh" );
1492         }
1493     
1494         displayModes = (NSArray *)CGDisplayAvailableModes(glw_state.display);
1495         if (!displayModes) {
1496                 common->Error( "CGDisplayAvailableModes returned NULL -- 0x%0x is an invalid display", glw_state.display);
1497         }
1498     
1499         modeCount = [displayModes count];
1500         if (verbose) {
1501                 common->Printf( "%d modes avaliable\n", modeCount);
1502                 common->Printf( "Current mode is %s\n", [[(id)CGDisplayCurrentMode(glw_state.display) description] cString]);
1503         }
1504     
1505         // Default to the current desktop mode
1506         bestModeIndex = 0xFFFFFFFF;
1507     
1508         for ( modeIndex = 0; modeIndex < modeCount; ++modeIndex ) {
1509                 id object;
1510                 int refresh;
1511         
1512                 mode = [displayModes objectAtIndex: modeIndex];
1513                 if (verbose) {
1514                         common->Printf( " mode %d -- %s\n", modeIndex, [[mode description] cString]);
1515                 }
1516
1517                 // Make sure we get the right size
1518                 if ([[mode objectForKey: (id)kCGDisplayWidth] intValue] != parms.width ||
1519                                 [[mode objectForKey: (id)kCGDisplayHeight] intValue] != parms.height) {
1520                         if (verbose)
1521                                 common->Printf( " -- bad size\n");
1522                         continue;
1523                 }
1524
1525                 // Make sure that our frequency restrictions are observed
1526                 refresh = [[mode objectForKey: (id)kCGDisplayRefreshRate] intValue];
1527                 if (minFreq &&  refresh < minFreq) {
1528                         if (verbose)
1529                                 common->Printf( " -- refresh too low\n");
1530                         continue;
1531                 }
1532
1533                 if (maxFreq && refresh > maxFreq) {
1534                         if (verbose)
1535                                 common->Printf( " -- refresh too high\n");
1536                         continue;
1537                 }
1538
1539                 if ([[mode objectForKey: (id)kCGDisplayBitsPerPixel] intValue] != colorDepth) {
1540                         if (verbose)
1541                                 common->Printf( " -- bad depth\n");
1542                         continue;
1543                 }
1544
1545                 object = [mode objectForKey: (id)kCGDisplayModeIsStretched];
1546                 if ( object ) {
1547                         if ( [object boolValue] != cvarSystem->GetCVarBool( "r_stretched" ) ) {
1548                                 if (verbose)
1549                                         common->Printf( " -- bad stretch setting\n");
1550                                 continue;
1551                         }
1552                 }
1553                 else {
1554                         if ( cvarSystem->GetCVarBool( "r_stretched" ) ) {
1555                                 if (verbose)
1556                                         common->Printf( " -- stretch requested, stretch property not available\n");
1557                                 continue;
1558                         }
1559                 }
1560                 
1561                 bestModeIndex = modeIndex;
1562                 if (verbose)
1563                         common->Printf( " -- OK\n", bestModeIndex);
1564         }
1565
1566         if (verbose)
1567                 common->Printf( " bestModeIndex = %d\n", bestModeIndex);
1568
1569         if (bestModeIndex == 0xFFFFFFFF) {
1570                 common->Printf( "No suitable display mode available.\n");
1571                 return nil;
1572         }
1573     
1574         return [displayModes objectAtIndex: bestModeIndex];
1575 }
1576
1577
1578 #define MAX_DISPLAYS 128
1579
1580 void Sys_GetGammaTable(glwgamma_t *table) {
1581         CGTableCount tableSize = 512;
1582         CGDisplayErr err;
1583     
1584         table->tableSize = tableSize;
1585         if (table->red)
1586                 free(table->red);
1587         table->red = (float *)malloc(tableSize * sizeof(*table->red));
1588         if (table->green)
1589                 free(table->green);
1590         table->green = (float *)malloc(tableSize * sizeof(*table->green));
1591         if (table->blue)
1592                 free(table->blue);
1593         table->blue = (float *)malloc(tableSize * sizeof(*table->blue));
1594     
1595         // TJW: We _could_ loop here if we get back the same size as our table, increasing the table size.
1596         err = CGGetDisplayTransferByTable(table->display, tableSize, table->red, table->green, table->blue,
1597                                                                                                                                                 &table->tableSize);
1598         if (err != CGDisplayNoErr) {
1599                 common->Printf("GLimp_Init: CGGetDisplayTransferByTable returned %d.\n", err);
1600                 table->tableSize = 0;
1601         }
1602 }
1603
1604 void Sys_SetGammaTable(glwgamma_t *table) { }
1605
1606 void Sys_StoreGammaTables() {
1607         // Store the original gamma for all monitors so that we can fade and unfade them all
1608         CGDirectDisplayID displays[MAX_DISPLAYS];
1609         CGDisplayCount displayIndex;
1610         CGDisplayErr err;
1611
1612         err = CGGetActiveDisplayList(MAX_DISPLAYS, displays, &glw_state.displayCount);
1613         if (err != CGDisplayNoErr)
1614                 Sys_Error("Cannot get display list -- CGGetActiveDisplayList returned %d.\n", err);
1615     
1616         glw_state.originalDisplayGammaTables = (glwgamma_t *)calloc(glw_state.displayCount, sizeof(*glw_state.originalDisplayGammaTables));
1617         for (displayIndex = 0; displayIndex < glw_state.displayCount; displayIndex++) {
1618                 glwgamma_t *table;
1619
1620                 table = &glw_state.originalDisplayGammaTables[displayIndex];
1621                 table->display = displays[displayIndex];
1622                 Sys_GetGammaTable(table);
1623         }
1624 }
1625
1626
1627 //  This isn't a mathematically correct fade, but we don't care that much.
1628 void Sys_SetScreenFade(glwgamma_t *table, float fraction) {
1629         CGTableCount tableSize;
1630         CGGammaValue *red, *blue, *green;
1631         CGTableCount gammaIndex;
1632     
1633         //    if (!glConfig.deviceSupportsGamma)
1634         //        return;
1635
1636         if (!(tableSize = table->tableSize))
1637                 // we couldn't get the table for this display for some reason
1638                 return;
1639     
1640         //    common->Printf("0x%08x %f\n", table->display, fraction);
1641     
1642         red = glw_state.tempTable.red;
1643         green = glw_state.tempTable.green;
1644         blue = glw_state.tempTable.blue;
1645         if (glw_state.tempTable.tableSize < tableSize) {
1646                 glw_state.tempTable.tableSize = tableSize;
1647                 red = (float *)realloc(red, sizeof(*red) * tableSize);
1648                 green = (float *)realloc(green, sizeof(*green) * tableSize);
1649                 blue = (float *)realloc(blue, sizeof(*blue) * tableSize);
1650                 glw_state.tempTable.red = red;
1651                 glw_state.tempTable.green = green;
1652                 glw_state.tempTable.blue = blue;
1653         }
1654
1655         for (gammaIndex = 0; gammaIndex < table->tableSize; gammaIndex++) {
1656                 red[gammaIndex] = table->red[gammaIndex] * fraction;
1657                 blue[gammaIndex] = table->blue[gammaIndex] * fraction;
1658                 green[gammaIndex] = table->green[gammaIndex] * fraction;
1659         }
1660     
1661         CGSetDisplayTransferByTable(table->display, table->tableSize, red, green, blue);
1662 }
1663
1664 // Fades all the active displays at the same time.
1665
1666 #define FADE_DURATION 0.5
1667 void Sys_FadeScreens() {
1668         CGDisplayCount displayIndex;
1669         int stepIndex;
1670         glwgamma_t *table;
1671         NSTimeInterval start, current;
1672         float time;
1673     
1674         //   if (!glConfig.deviceSupportsGamma)
1675         //        return;
1676
1677         common->Printf("Fading all displays\n");
1678     
1679         start = [NSDate timeIntervalSinceReferenceDate];
1680         time = 0.0;
1681         while (time != FADE_DURATION) {
1682                 current = [NSDate timeIntervalSinceReferenceDate];
1683                 time = current - start;
1684                 if (time > FADE_DURATION)
1685                         time = FADE_DURATION;
1686             
1687                 for (displayIndex = 0; displayIndex < glw_state.displayCount; displayIndex++) {            
1688                         table = &glw_state.originalDisplayGammaTables[displayIndex];
1689                         Sys_SetScreenFade(table, 1.0 - time / FADE_DURATION);
1690                 }
1691         }
1692 }
1693
1694 void Sys_FadeScreen(CGDirectDisplayID display) {
1695         CGDisplayCount displayIndex;
1696         glwgamma_t *table;
1697         int stepIndex;
1698
1699         common->Printf( "FIXME: Sys_FadeScreen\n" );
1700     
1701         return;
1702     
1703         //    if (!glConfig.deviceSupportsGamma)
1704         //        return;
1705
1706         common->Printf("Fading display 0x%08x\n", display);
1707
1708         for (displayIndex = 0; displayIndex < glw_state.displayCount; displayIndex++) {
1709                 if (display == glw_state.originalDisplayGammaTables[displayIndex].display) {
1710                         NSTimeInterval start, current;
1711                         float time;
1712             
1713                         start = [NSDate timeIntervalSinceReferenceDate];
1714                         time = 0.0;
1715
1716                         table = &glw_state.originalDisplayGammaTables[displayIndex];
1717                         while (time != FADE_DURATION) {
1718                                 current = [NSDate timeIntervalSinceReferenceDate];
1719                                 time = current - start;
1720                                 if (time > FADE_DURATION)
1721                                         time = FADE_DURATION;
1722
1723                                 Sys_SetScreenFade(table, 1.0 - time / FADE_DURATION);
1724                         }
1725                         return;
1726                 }
1727         }
1728
1729         common->Printf("Unable to find display to fade it\n");
1730 }
1731
1732 void Sys_UnfadeScreens() {
1733         CGDisplayCount displayIndex;
1734         int stepIndex;
1735         glwgamma_t *table;
1736         NSTimeInterval start, current;
1737         float time;
1738
1739         common->Printf( "FIXME: Sys_UnfadeScreens\n" );
1740     
1741         return;
1742     
1743         //    if (!glConfig.deviceSupportsGamma)
1744         //        return;
1745         
1746         common->Printf("Unfading all displays\n");
1747
1748         start = [NSDate timeIntervalSinceReferenceDate];
1749         time = 0.0;
1750         while (time != FADE_DURATION) {
1751                 current = [NSDate timeIntervalSinceReferenceDate];
1752                 time = current - start;
1753                 if (time > FADE_DURATION)
1754                         time = FADE_DURATION;
1755             
1756                 for (displayIndex = 0; displayIndex < glw_state.displayCount; displayIndex++) {            
1757                         table = &glw_state.originalDisplayGammaTables[displayIndex];
1758                         Sys_SetScreenFade(table, time / FADE_DURATION);
1759                 }
1760         }
1761 }
1762
1763 void Sys_UnfadeScreen(CGDirectDisplayID display, glwgamma_t *table) {
1764         CGDisplayCount displayIndex;
1765         int stepIndex;
1766         
1767         common->Printf( "FIXME: Sys_UnfadeScreen\n" );
1768
1769         return;
1770         //    if (!glConfig.deviceSupportsGamma)
1771         //        return;
1772     
1773         common->Printf("Unfading display 0x%08x\n", display);
1774
1775         if (table) {
1776                 CGTableCount i;
1777         
1778                 common->Printf("Given table:\n");
1779                 for (i = 0; i < table->tableSize; i++) {
1780                         common->Printf("  %f %f %f\n", table->red[i], table->blue[i], table->green[i]);
1781                 }
1782         }
1783     
1784         // Search for the original gamma table for the display
1785         if (!table) {
1786                 for (displayIndex = 0; displayIndex < glw_state.displayCount; displayIndex++) {
1787                         if (display == glw_state.originalDisplayGammaTables[displayIndex].display) {
1788                                 table = &glw_state.originalDisplayGammaTables[displayIndex];
1789                                 break;
1790                         }
1791                 }
1792         }
1793     
1794         if (table) {
1795                 NSTimeInterval start, current;
1796                 float time;
1797         
1798                 start = [NSDate timeIntervalSinceReferenceDate];
1799                 time = 0.0;
1800
1801                 while (time != FADE_DURATION) {
1802                         current = [NSDate timeIntervalSinceReferenceDate];
1803                         time = current - start;
1804                         if (time > FADE_DURATION)
1805                                 time = FADE_DURATION;
1806                         Sys_SetScreenFade(table, time / FADE_DURATION);
1807                 }
1808                 return;
1809         }
1810     
1811         common->Printf("Unable to find display to unfade it\n");
1812 }
1813
1814 #define MAX_DISPLAYS 128
1815
1816 CGDirectDisplayID Sys_DisplayToUse(void) {
1817         static bool                                     gotDisplay =  NO;
1818         static CGDirectDisplayID        displayToUse;
1819     
1820         CGDisplayErr                            err;
1821         CGDirectDisplayID                       displays[MAX_DISPLAYS];
1822         CGDisplayCount                          displayCount;
1823         int                                                     displayIndex;
1824     
1825         if ( gotDisplay ) {
1826                 return displayToUse;
1827         }
1828         gotDisplay = YES;    
1829     
1830         err = CGGetActiveDisplayList( MAX_DISPLAYS, displays, &displayCount );
1831         if ( err != CGDisplayNoErr ) {
1832                 common->Error("Cannot get display list -- CGGetActiveDisplayList returned %d.\n", err );
1833         }
1834
1835         // -1, the default, means to use the main screen
1836         displayIndex = r_screen.GetInteger();
1837         
1838         if ( displayIndex < 0 || displayIndex >= displayCount ) {
1839                 // This is documented (in CGDirectDisplay.h) to be the main display.  We want to
1840                 // return this instead of kCGDirectMainDisplay since this will allow us to compare
1841                 // display IDs.
1842                 displayToUse = displays[ 0 ];
1843         } else {
1844                 displayToUse = displays[ displayIndex ];
1845         }
1846
1847         return displayToUse;
1848 }
1849
1850 /*
1851 ===================
1852 GLimp_SetScreenParms
1853 ===================
1854 */
1855 bool GLimp_SetScreenParms( glimpParms_t parms ) {
1856         return true;
1857 }
1858
1859 /*
1860 ===================
1861 Sys_GrabMouseCursor
1862 ===================
1863 */
1864 void Sys_GrabMouseCursor( bool grabIt ) { }
1865