//========= Copyright  1996-2009, Valve Corporation, All rights reserved. ============//
//
// Purpose: Defines a group of app systems that all have the same lifetime
// that need to be connected/initialized, etc. in a well-defined order
//
// $Revision: $
// $NoKeywords: $
//=============================================================================//

#include <Cocoa/Cocoa.h>
#include <OpenGL/OpenGL.h>
#include <OpenGL/gl.h>
#include <OpenGL/glext.h>
#include <IOKit/IOKitLib.h>


#undef MIN
#undef MAX
#define DONT_DEFINE_BOOL	// Don't define BOOL!
#include "tier0/threadtools.h"
#include "tier0/icommandline.h"
#include "tier1/interface.h"
#include "tier1/strtools.h"
#include "tier1/utllinkedlist.h"
#include "togl/rendermechanism.h"
#include "appframework/ilaunchermgr.h"	// gets pulled in from glmgr.h
#include "appframework/iappsystemgroup.h"
#include "inputsystem/ButtonCode.h"


// some helper functions, relocated out of GLM since they are used here

// this one makes a new context
bool	GLMDetectSLGU( void );
bool	GLMDetectSLGU( void )
{
	CGLError	cgl_error = (CGLError)0;
	bool		result = false;
	
	CGLContextObj oldctx = CGLGetCurrentContext();

	static CGLPixelFormatAttribute attribs[] = 
	{
		kCGLPFADoubleBuffer,
		kCGLPFANoRecovery,
		kCGLPFAAccelerated,
		kCGLPFADepthSize,
			(CGLPixelFormatAttribute)0,
		kCGLPFAColorSize,
			(CGLPixelFormatAttribute)32,

		(CGLPixelFormatAttribute)0	// list term
	};

	CGLPixelFormatObj	pixfmtobj = NULL;
	GLint				npix;
	
	CGLContextObj		ctxobj = NULL;
	
	cgl_error = CGLChoosePixelFormat( attribs, &pixfmtobj, &npix );
	if (!cgl_error)
	{
		// got pixel format, make a context
		
		cgl_error = CGLCreateContext( pixfmtobj, NULL, &ctxobj );
		if (!cgl_error)
		{
			CGLSetCurrentContext( ctxobj );

			// now do the test

			_CGLContextParameter	kCGLCPGCDMPEngine = ((_CGLContextParameter)1314);

			GLint dummyval = 0;
			cgl_error = CGLGetParameter( CGLGetCurrentContext(), kCGLCPGCDMPEngine, &dummyval );

			result = (!cgl_error);
			
			// all done, go back to old context, and destroy the temp one
			CGLSetCurrentContext( oldctx );
			CGLDestroyContext( ctxobj );
		}
		
		// destroy the pixel format obj
		CGLDestroyPixelFormat( pixfmtobj );
	}

	return result;
}


bool	GLMDetectScaledResolveMode( uint osComboVersion, bool hasSLGU );
bool	GLMDetectScaledResolveMode( uint osComboVersion, bool hasSLGU )
{
	bool result = false;
	
	// note this function assumes a current context on the renderer in question
	// and that FB blit and SLGU are present..
	
	if (!hasSLGU)
		return false;
		
	if (osComboVersion <= 0x000A0604)	// we know no one has it before 10.6.5
		return false;
	
	// in 10.6.6 and later, just check for the ext string.
	char *gl_ext_string = (char*)glGetString(GL_EXTENSIONS);
	// avoid crashing due to strstr'ing NULL pointer returned from glGetString
	if (!gl_ext_string)
		gl_ext_string = "";
	
	result = strstr(gl_ext_string, "GL_EXT_framebuffer_multisample_blit_scaled") != NULL;
	
	if ( !result )
	{
		// make two FBO's
		GLuint	fbos[2];
		GLuint	rbos[2];
		int extent = 64;
		
		// make two render buffers

		for( int fbi = 0; fbi < 2; fbi++ )
		{
			glGenFramebuffersEXT( 1, &fbos[fbi] ); CheckGLError( __LINE__ );
			glBindFramebufferEXT( fbi ? GL_DRAW_FRAMEBUFFER_EXT : GL_READ_FRAMEBUFFER_EXT , fbos[fbi] );  CheckGLError( __LINE__ );
			
			glGenRenderbuffersEXT( 1, &rbos[fbi] ); CheckGLError( __LINE__ );
			glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, rbos[fbi] ); CheckGLError( __LINE__ );

			// make it multisampled if 0
			if (!fbi)
			{
				glRenderbufferStorageMultisampleEXT( GL_RENDERBUFFER_EXT, 2, GL_RGBA8, extent,extent );	  CheckGLError( __LINE__ );
			}
			else
			{
				glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_RGBA8, extent,extent ); CheckGLError( __LINE__ );
			}

			// attach it 
			// #0 gets to be read and multisampled
			// #1 gets to be draw and multisampled
			glFramebufferRenderbufferEXT( fbi ? GL_DRAW_FRAMEBUFFER_EXT : GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, rbos[fbi] ); CheckGLError( __LINE__ );
		}
		// now test
		while( glGetError() )	// clear error queue
		{
			;
		}

		// now do the dummy blit
		glBlitFramebufferEXT(	0,0,extent,extent, 0,0,extent,extent, GL_COLOR_BUFFER_BIT, XGL_SCALED_RESOLVE_FASTEST_EXT );

		// type of error we get back lets us know what the outcome is.
		// invalid enum error								-> unsupported
		// no error or invalid op							-> supported

		GLenum errorcode = (GLenum)glGetError();
		switch(errorcode)
		{
			// expected outcomes.
			
			// positive
			case GL_NO_ERROR:
			case GL_INVALID_OPERATION:
				result = true;			// new scaled resolve detected
				break;
				
			default:
				result = false;			// no scaled resolve
				break;
		}
		
		// unbind and wipe stuff
		
		glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); CheckGLError( __LINE__ );
		
		for( int xfbi = 0; xfbi < 2; xfbi++ )
		{
			// unbind FBO
			glBindFramebufferEXT( xfbi ? GL_DRAW_FRAMEBUFFER_EXT : GL_READ_FRAMEBUFFER_EXT , 0 ); CheckGLError( __LINE__ );

			// del FBO and RBO
			glDeleteFramebuffersEXT( 1, &fbos[xfbi] ); CheckGLError( __LINE__ );
			glDeleteRenderbuffersEXT( 1, &rbos[xfbi] ); CheckGLError( __LINE__ );
		}
	}

	return result; // no SLGU, no scaled resolve blit even possible
}

//===============================================================================

GLMRendererInfo::GLMRendererInfo( GLMRendererInfoFields *info )
{
	NSAutoreleasePool	*tempPool = [[NSAutoreleasePool alloc] init ];

	// absorb info obtained so far by caller
	m_info = *info;
	m_displays = NULL;

	// gather more info using a dummy context
	unsigned int attribs[] = 
	{
		kCGLPFADoubleBuffer, kCGLPFANoRecovery, kCGLPFAAccelerated,
		kCGLPFADepthSize, 0,
		kCGLPFAColorSize, 32,
		kCGLPFARendererID, info->m_rendererID,
		0
	};

	NSOpenGLPixelFormat	*pixFmt		=	[[NSOpenGLPixelFormat alloc] initWithAttributes:(NSOpenGLPixelFormatAttribute*)attribs]; 
	NSOpenGLContext		*nsglCtx	=	[[NSOpenGLContext alloc] initWithFormat: pixFmt shareContext: NULL ];

	[nsglCtx makeCurrentContext];
		
	// run queries.
	char *gl_ext_string = (char*)glGetString(GL_EXTENSIONS);

	uint vers = m_info.m_osComboVersion;
	// avoid crashing due to strstr'ing NULL pointer returned from glGetString
	if (!gl_ext_string)
	  gl_ext_string = "";

	// effectively blacklist the renderer if it doesn't actually work; sort it to back of list
	if ( !nsglCtx )
	{
		m_info.m_vidMemory = 1;
		m_info.m_texMemory = 1;
	}
	
	//-------------------------------------------------------------------
	// booleans
	//-------------------------------------------------------------------
	// gamma writes.
	m_info.m_hasGammaWrites = true;
	if ( vers < 0x000A0600 )				// pre 10.6.0, no SRGB write - see http://developer.apple.com/graphicsimaging/opengl/capabilities/GLInfo_1058.html
	{
		m_info.m_hasGammaWrites = false;
	}
	
	if (m_info.m_atiR5xx)
	{
		m_info.m_hasGammaWrites = false;	// it just don't, even post 10.6.3
	}
	
	// if CLI option for fake SRGB mode is enabled, turn off this cap, act like we do not have EXT FB SRGB
	if (CommandLine()->FindParm("-glmenablefakesrgb"))
	{
		m_info.m_hasGammaWrites = false;
	}
	
	// extension string *could* be checked, but on 10.6.3 the ext string is not there, but the func *is*

	//-------------------------------------------------------------------
	// mixed attach sizes for FBO
	m_info.m_hasMixedAttachmentSizes = true;
	if ( vers < 0x000A0603 )	// pre 10.6.3, no mixed attach sizes
	{
		m_info.m_hasMixedAttachmentSizes = false;
	}
	else
	{
		if (!strstr(gl_ext_string, "GL_ARB_framebuffer_object"))
		{
			// ARB_framebuffer_object not available
			m_info.m_hasMixedAttachmentSizes = false;
		}
	}
	// also check ext string

	//-------------------------------------------------------------------
	// BGRA vert attribs
	m_info.m_hasBGRA = true;
	if ( vers < 0x000A0603 )	// pre 10.6.3, no BGRA attribs
	{
		m_info.m_hasBGRA = false;
	}
	else
	{
		if (!strstr(gl_ext_string, "EXT_vertex_array_bgra"))
		{
			// EXT_vertex_array_bgra not available
			m_info.m_hasBGRA = false;
		}
	}

	//-------------------------------------------------------------------
	m_info.m_hasNewFullscreenMode = true;
	if ( vers < 0x000A0600 )	// pre 10.6.0, no clever window server full screen mode
	{
		m_info.m_hasNewFullscreenMode = false;
	}
	
	//-------------------------------------------------------------------
	m_info.m_hasNativeClipVertexMode = true;
	// this one uses a heuristic, and allows overrides in case the heuristic is wrong
	// or someone wants to try a beta driver or something.

	// known bad combinations get turned off here..
	
	// any ATI hardware...
	// TURNED OFF OS CHECK if (m_info.m_osComboVersion <= 0x000A0603)
	// still believe to be broken in 10.6.4
	{
		if (m_info.m_ati)
		{
			m_info.m_hasNativeClipVertexMode = false;
		}
	}
	
	// R500, forever..
	if (m_info.m_atiR5xx)
	{
		m_info.m_hasNativeClipVertexMode = false;
	}

	// if user disabled them
	if (CommandLine()->FindParm("-glmdisableclipplanes"))
	{
		m_info.m_hasNativeClipVertexMode = false;
	}
	
	// or maybe enabled them..
	if (CommandLine()->FindParm("-glmenableclipplanes"))
	{
		m_info.m_hasNativeClipVertexMode = true;
	}
	
	//-------------------------------------------------------------------
	m_info.m_hasOcclusionQuery = true;
	if (!strstr(gl_ext_string, "ARB_occlusion_query"))
	{
		m_info.m_hasOcclusionQuery = false;		// you don't got it!
	}
	
	//-------------------------------------------------------------------
	m_info.m_hasFramebufferBlit = true;
	if (!strstr(gl_ext_string, "EXT_framebuffer_blit"))
	{
		m_info.m_hasFramebufferBlit = false;	// you know you don't got it!
	}
	
	//-------------------------------------------------------------------
	m_info.m_maxAniso = 4;			//FIXME needs real query
	
	//-------------------------------------------------------------------
	m_info.m_hasBindableUniforms = true;
	if (!strstr(gl_ext_string, "EXT_bindable_uniform"))
	{
		m_info.m_hasBindableUniforms = false;
	}
	m_info.m_hasBindableUniforms = false;		// hardwiring this path to false until we see how to accelerate it properly
	
	//-------------------------------------------------------------------
	m_info.m_hasUniformBuffers = true;
	if (!strstr(gl_ext_string, "ARB_uniform_buffer"))
	{
		m_info.m_hasUniformBuffers = false;
	}

	//-------------------------------------------------------------------
	// test for performance pack (10.6.4+)

	bool perfPackageDetected = GLMDetectSLGU();
	
	if (perfPackageDetected)
	{
		m_info.m_hasPerfPackage1 = true;
	}	

	if (CommandLine()->FindParm("-glmenableperfpackage"))	// force it on
	{
		m_info.m_hasPerfPackage1 = true;
	}
	
	if (CommandLine()->FindParm("-glmdisableperfpackage"))	// force it off
	{
		m_info.m_hasPerfPackage1 = false;
	}


	//-------------------------------------------------------------------
	// runtime options that aren't negotiable once set

	m_info.m_hasDualShaders = CommandLine()->FindParm("-glmdualshaders");

	//-------------------------------------------------------------------
	// "can'ts "
	
	m_info.m_cantBlitReliably = (m_info.m_osComboVersion < 0x000A0606) && m_info.m_intel;		//don't trust FBO blit on Intel before 10.6.6
	if (CommandLine()->FindParm("-glmenabletrustblit"))
	{
		m_info.m_cantBlitReliably = false;			// we trust the blit, so set the cant-blit cap to false
	}
	if (CommandLine()->FindParm("-glmdisabletrustblit"))
	{
		m_info.m_cantBlitReliably = true;			// we do not trust the blit, so set the cant-blit cap to true
	}

	//m_info.m_cantAttachSRGB = (m_info.m_nv && m_info.m_osComboVersion < 0x000A0600);	//NV drivers won't accept SRGB tex on an FBO color target in 10.5.8
	//m_info.m_cantAttachSRGB = (m_info.m_ati && m_info.m_osComboVersion < 0x000A0600);	//... does ATI have the same problem?
	m_info.m_cantAttachSRGB = (m_info.m_osComboVersion < 0x000A0600);	// across the board on 10.5.x actually..

	// MSAA resolve issues
	m_info.m_cantResolveFlipped	= false;	// initial stance
	
	if (m_info.m_ati)
	{
		//Jan 2011 - ATI says it's better to do two step blit than to try and resolve upside down
		m_info.m_cantResolveFlipped = true;
	}
	
	if (m_info.m_nv)
	{
		// we're going to mark it 'broken' unless perf package 1 (10.6.4+) is present
		if (!m_info.m_hasPerfPackage1)
		{
			m_info.m_cantResolveFlipped = true;
		}
	}
	
	// this is just the private assessment of whather scaled resolve is available.
	// the activation of it will stay tied to the gl_minify_resolve_mode / gl_magnify_resolve_mode convars in glmgr
	if 	(m_info.m_osComboVersion > 0x000A0700 || CommandLine()->FindParm("-gl_enable_scaled_resolve") )
	{
		bool scaledResolveDetected = GLMDetectScaledResolveMode( m_info.m_osComboVersion, m_info.m_hasPerfPackage1 );
		m_info.m_cantResolveScaled = !scaledResolveDetected;
	}
	else
	{
		m_info.m_cantResolveScaled = true;
	}

	// and you can force it to be "available" if you really want to..
	if ( CommandLine()->FindParm("-gl_force_enable_scaled_resolve") )
	{
		m_info.m_cantResolveScaled = false;
	}
	
	// gamma decode impacting shader codegen
	m_info.m_costlyGammaFlips = false;
	if (m_info.m_osComboVersion < 0x000A0600)		// if Leopard
		m_info.m_costlyGammaFlips = true;
		
	if (m_info.m_atiR5xx)							// or r5xx - always
		m_info.m_costlyGammaFlips = true;

	if ( (m_info.m_atiR6xx) && (m_info.m_osComboVersion < 0x000A0605) )	// or r6xx prior to 10.6.5
		m_info.m_costlyGammaFlips = true;

	// The OpenGL driver for Intel HD4000 on 10.8 has a bug in the GLSL compiler, which was fixed
	// in 10.9 (and unlikely to be fixed in 10.8). See intelglmallocworkaround.h for more info.
	bool mountainLion = (m_info.m_osComboVersion >= 0x000A0800) && (m_info.m_osComboVersion < 0x000A0900);
	m_info.m_badDriver108Intel = mountainLion && m_info.m_intelHD4000;
	if ( CommandLine()->FindParm("-glmenablemallocworkaround") )
	{
		m_info.m_badDriver108Intel = true;
	}
	if ( CommandLine()->FindParm("-glmdisablemallocworkaround") )
	{
		m_info.m_badDriver108Intel = false;
	}

	[nsglCtx release];
	[pixFmt release];
	
	[tempPool release];
}

GLMRendererInfo::~GLMRendererInfo( void )
{
	if (m_displays)
	{
		// delete all the new'd renderer infos that the table tracks
		FOR_EACH_VEC( *m_displays, i )
		{
			delete (*this->m_displays)[i];
		}
		delete m_displays;
		m_displays = NULL;
	}
}

extern "C" int DisplayInfoSortFunction( GLMDisplayInfo* const *A, GLMDisplayInfo* const *B )
{
	int bigger = -1;
	int smaller = 1;	// adjust these to get the ordering you want

	// check main-ness - main should win

	uint maskOfMainDisplay = CGDisplayIDToOpenGLDisplayMask( CGMainDisplayID() );
	//Assert( maskOfMainDisplay==1 );	// just curious
	
	int mainscreena = (*A)->m_info.m_glDisplayMask & maskOfMainDisplay;
	int mainscreenb = (*B)->m_info.m_glDisplayMask & maskOfMainDisplay;
	
	if ( mainscreena > mainscreenb )
	{
		return bigger;
	}
	else if ( mainscreena < mainscreenb )
	{
		return smaller;
	}
	
	// check area - larger screen should win
	int areaa = (*A)->m_info.m_displayPixelWidth * (*A)->m_info.m_displayPixelHeight;
	int areab = (*B)->m_info.m_displayPixelWidth * (*B)->m_info.m_displayPixelHeight;

	if ( areaa > areab )
	{	
		return bigger;
	}
	else if ( areaa < areab )
	{
		return smaller;
	}
	
	return 0;	// equal rank
}


void	GLMRendererInfo::PopulateDisplays( void )
{
	Assert( !m_displays );
	m_displays = new CUtlVector< GLMDisplayInfo* >;
	
	for( int i=0; i<32; i++)
	{
		// check mask to see if the selected display intersects this renderer
		CGOpenGLDisplayMask dspMask = (CGOpenGLDisplayMask)(1<<i);
		
		if ( m_info.m_displayMask & dspMask )
		{
			// exclude teeny displays (they may represent offline displays)
			// exclude inactive displays
			
			CGDirectDisplayID cgid = CGOpenGLDisplayMaskToDisplayID ( dspMask );

			if ( (cgid != kCGNullDirectDisplay) && CGDisplayIsActive( cgid ) && (CGDisplayPixelsWide( cgid ) >= 512) && (CGDisplayPixelsHigh( cgid ) >= 384) )
			{
				GLMDisplayInfo *newdisp = new GLMDisplayInfo( cgid, dspMask );
				m_displays->AddToTail( newdisp );			
			}
		}
	}
	
	// now sort the table of displays.
	m_displays->Sort( DisplayInfoSortFunction );

	// then go back and ask each display to populate its display mode table.
	FOR_EACH_VEC( *m_displays, i )
	{
		(*this->m_displays)[i]->PopulateModes();
	}
}

const char *CheesyRendererDecode( uint value )
{
	switch(value)
	{
		case 0x00020200 :  return "Generic";
		case 0x00020400 :  return "GenericFloat";
		case 0x00020600 :  return "AppleSW";
		case 0x00021000 :  return "ATIRage128";
		case 0x00021200 :  return "ATIRadeon";
		case 0x00021400 :  return "ATIRagePro";
		case 0x00021600 :  return "ATIRadeon8500";
		case 0x00021800 :  return "ATIRadeon9700";
		case 0x00021900 :  return "ATIRadeonX1000";
		case 0x00021A00 :  return "ATIRadeonX2000";
		case 0x00022000 :  return "NVGeForce2MX";
		case 0x00022200 :  return "NVGeForce3";
		case 0x00022400 :  return "NVGeForceFX";
		case 0x00022600 :  return "NVGeForce8xxx";
		case 0x00023000 :  return "VTBladeXP2";
		case 0x00024000 :  return "Intel900";
		case 0x00024200 :  return "IntelX3100";
		case 0x00040000 :  return "Mesa3DFX";

		default: return "UNKNOWN";
	}
}

extern const char *GLMDecode( GLMThing_t thingtype, unsigned long value );

void	GLMRendererInfo::Dump( int which )
{
	GLMPRINTF(("\n     #%d: GLMRendererInfo @ %08x, renderer-id=%s(%08x)  display-mask=%08x  vram=%dMB",
		which, this,
		CheesyRendererDecode( m_info.m_rendererID & 0x00FFFF00 ), m_info.m_rendererID,
		m_info.m_displayMask,
		m_info.m_vidMemory >> 20
	));
	GLMPRINTF(("\n       VendorID=%04x  DeviceID=%04x  Model=%s",
		m_info.m_pciVendorID,
		m_info.m_pciDeviceID,
		m_info.m_pciModelString
	));

	FOR_EACH_VEC( *m_displays, i )
	{
		(*m_displays)[i]->Dump(i);
	}
}


//===============================================================================


GLMDisplayDB::GLMDisplayDB	( void )
{
	m_renderers = NULL;	
}

GLMDisplayDB::~GLMDisplayDB	( void )
{
	if (m_renderers)
	{
		// delete all the new'd renderer infos that the table tracks
		FOR_EACH_VEC( *m_renderers, i )
		{
			delete (*this->m_renderers)[i];
		}
		delete m_renderers;
		m_renderers = NULL;
	}
}

extern "C" int RendererInfoSortFunction( GLMRendererInfo * const *A, GLMRendererInfo* const *B )
{
	int bigger = -1;
	int smaller = 1;
	
	// check VRAM
	if ( (*A)->m_info.m_vidMemory > (*B)->m_info.m_vidMemory )
	{	
		return bigger;
	}
	else if ( (*A)->m_info.m_vidMemory < (*B)->m_info.m_vidMemory )
	{
		return smaller;
	}
	
	// check MSAA limit
	if ( (*A)->m_info.m_maxSamples > (*B)->m_info.m_maxSamples )
	{	
		return bigger;
	}
	else if ( (*A)->m_info.m_maxSamples < (*B)->m_info.m_maxSamples )
	{
		return smaller;
	}
	
	/*
		// this was not a great idea here..
		
		// check if one has the main screen - is that index 0 in all cases?
		uint maskOfMainDisplay = CGDisplayIDToOpenGLDisplayMask( CGMainDisplayID() );
		Assert( maskOfMainDisplay==1 );	// just curious
		
		int mainscreena = (*A)->m_info.m_displayMask & maskOfMainDisplay;
		int mainscreenb = (*B)->m_info.m_displayMask & maskOfMainDisplay;
		
		if ( mainscreena > mainscreenb )
		{
			return bigger;
		}
		else if ( mainscreena < mainscreenb )
		{
			return smaller;
		}
	*/
	
	return 0;	// equal rank
}

/** some code that NV gave us.  more generalized approach below..

		static io_registry_entry_t lookup_dev_NV(char *name)
		{
			mach_port_t master_port = 0;
			io_iterator_t iterator;
			io_registry_entry_t nub = 0;
			kern_return_t ret;

			IOMasterPort(MACH_PORT_NULL, &master_port);

			ret = IOServiceGetMatchingServices(master_port, IOServiceMatching(name), &iterator);

			if (iterator) {
				nub = IOIteratorNext(iterator);

				if (IOIteratorNext(iterator)) {
					printf("warning: more than one card?\n");
				}
				IOObjectRelease(iterator);
			}
			IOObjectRelease(master_port);

			return nub;
		}


		void	GetDriverInfoString_NV( char *driverNameBuf, int driverNameBufLen )
		{
			// courtesy NVIDIA dev rel
			
			io_registry_entry_t registry;
			kern_return_t ret;

			//
			// Get NVKernel / IOGLBundleName
			//

			registry = lookup_dev_NV("NVKernel");
			if (!registry) {
				fprintf(stderr, "error: could not find NVKernel IORegistry entry!\n");
				return;
			}

			CFMutableDictionaryRef entry;
			ret = IORegistryEntryCreateCFProperties(registry, &entry, kCFAllocatorDefault, 0);
			if (ret != kIOReturnSuccess) {
				fprintf(stderr, "error: could not create CFProperties dictionary!\n");
				return;
			}

			CFStringRef bundle_name_ref = (CFStringRef) CFDictionaryGetValue(entry, CFSTR("IOGLBundleName"));
			if (!bundle_name_ref) {
				fprintf(stderr, "error: could not get IOGLBundleName reference!\n");
				return;
			}

			const char *bundle_name = CFStringGetCStringPtr(bundle_name_ref, CFStringGetSystemEncoding());
			if (!bundle_name) {
				fprintf(stderr, "error: could not get IOGLBundleName!\n");
				return;
			}

			CFStringRef identifier = CFStringCreateWithFormat(NULL, NULL, CFSTR("com.apple.%s"), bundle_name);

			//
			// Get bundle information
			//

			CFBundleRef bundle;
			bundle = CFBundleGetBundleWithIdentifier(identifier);
			if (!bundle) {
				fprintf(stderr, "error: could not get GL driver bundle!\n");
				return;
			}

			CFDictionaryRef dict;
			CFStringRef info;

			dict = CFBundleGetInfoDictionary(bundle);
			if (!dict) {
				fprintf(stderr, "error: could not get bundle info dictionary!\n");
				return;
			}

			info = (CFStringRef) CFDictionaryGetValue(dict, CFSTR("CFBundleGetInfoString"));
			if (!info) {
				fprintf(stderr, "error: could not get CFBundleGetInfoString!\n");
				return;
			}

			CFStringGetCString(info, driverNameBuf, driverNameBufLen, CFStringGetSystemEncoding());

			IOObjectRelease(registry);
		}
**/

void	GLMDisplayDB::PopulateRenderers( void )
{
	Assert( !m_renderers );
	m_renderers = new CUtlVector< GLMRendererInfo* >;
	
	// now walk the renderer list
	// find the eligible ones and insert them into vector
	// if more than one, sort the vector by desirability with favorite at 0
	// then ask each renderer object to populate its displays

	// turns out how you have to do this is to walk the display mask 1<<n..
	// and query at each one, what renderers can hit that one.
	
	// when you find one, see if it's already in the vector above. if not, add it.
	// later, we sort them.
	
	for( int i=0; i<32; i++ )
	{
		CGLError			cgl_err		= (CGLError)0;
		CGLRendererInfoObj	cgl_rend	= NULL;
		GLint				nrend;
	
		CGOpenGLDisplayMask	dspMask		= (CGOpenGLDisplayMask)(1<<i);	
		CGDirectDisplayID	cgid		= CGOpenGLDisplayMaskToDisplayID( dspMask );

		bool selected = true;	// assume the best		

		if (selected)
		{
			if ( (cgid == kCGNullDirectDisplay)  || (!CGDisplayIsActive( cgid )) )
			{
				selected = false;
			}
		}

		if (selected)
		{
			cgl_err = CGLQueryRendererInfo( dspMask, &cgl_rend, &nrend );	// FIXME this call spams the console if you ask about an out of bounds display mask
																			// "<Error>: unknown error code: invalid display"
																			// we can fix that by getting the active display mask first.
			if (!cgl_err)
			{
				// walk the renderers that can hit this display
				// add to table if not already in table, and minimums met

				for( int j=0; j<nrend; j++)
				{
					int problems = 0;
					
					GLMRendererInfoFields	fields;
					memset( &fields, 0, sizeof(fields) );

					// early out if renderer ID already in the table
					cgl_err = CGLDescribeRenderer( cgl_rend, j,   kCGLRPRendererID, &fields.m_rendererID );			problems += (cgl_err != 0);
					cgl_err = CGLDescribeRenderer( cgl_rend, j,   kCGLRPDisplayMask, &fields.m_displayMask );		problems += (cgl_err != 0);

					cgl_err = CGLDescribeRenderer( cgl_rend, j,   kCGLRPFullScreen, &fields.m_fullscreen );			problems += (cgl_err != 0);
					cgl_err = CGLDescribeRenderer( cgl_rend, j,   kCGLRPAccelerated, &fields.m_accelerated );		problems += (cgl_err != 0);
					cgl_err = CGLDescribeRenderer( cgl_rend, j,   kCGLRPWindow, &fields.m_windowed );				problems += (cgl_err != 0);

					cgl_err = CGLDescribeRenderer( cgl_rend, j,   kCGLRPBufferModes, &fields.m_bufferModes );		problems += (cgl_err != 0);
					cgl_err = CGLDescribeRenderer( cgl_rend, j,   kCGLRPColorModes, &fields.m_colorModes );			problems += (cgl_err != 0);
					cgl_err = CGLDescribeRenderer( cgl_rend, j,   kCGLRPDepthModes, &fields.m_depthModes );			problems += (cgl_err != 0);
					cgl_err = CGLDescribeRenderer( cgl_rend, j,   kCGLRPStencilModes, &fields.m_stencilModes );		problems += (cgl_err != 0);
					cgl_err = CGLDescribeRenderer( cgl_rend, j,   kCGLRPMaxAuxBuffers, &fields.m_maxAuxBuffers );	problems += (cgl_err != 0);				
					cgl_err = CGLDescribeRenderer( cgl_rend, j,   kCGLRPMaxSampleBuffers, &fields.m_maxSampleBuffers );	problems += (cgl_err != 0);
					cgl_err = CGLDescribeRenderer( cgl_rend, j,   kCGLRPMaxSamples, &fields.m_maxSamples );			problems += (cgl_err != 0);
					cgl_err = CGLDescribeRenderer( cgl_rend, j,   kCGLRPSampleModes, &fields.m_sampleModes );		problems += (cgl_err != 0);
					cgl_err = CGLDescribeRenderer( cgl_rend, j,   kCGLRPSampleAlpha, &fields.m_sampleAlpha );		problems += (cgl_err != 0);
					cgl_err = CGLDescribeRenderer( cgl_rend, j,   kCGLRPVideoMemory, &fields.m_vidMemory );			problems += (cgl_err != 0);
					cgl_err = CGLDescribeRenderer( cgl_rend, j,   kCGLRPTextureMemory, &fields.m_texMemory );		problems += (cgl_err != 0);

					// Make sure the renderer is attached to a display.
					GLint online;
					cgl_err = CGLDescribeRenderer( cgl_rend, j,   kCGLRPOnline, &online );					problems += (cgl_err != 0);
					problems += ( online == 0 );

					// decide if this renderer goes in the table.

					bool	selected = !problems;
					
					if (selected)
					{
						// grab the OS version

						long vMajor = 0;	long vMinor = 0;	long vMinorMinor = 0;
						
						OSStatus gestalt_err = 0;
						gestalt_err = Gestalt(gestaltSystemVersionMajor, &vMajor);
						Assert(!gestalt_err);
						
						gestalt_err = Gestalt(gestaltSystemVersionMinor, &vMinor);
						Assert(!gestalt_err);

						gestalt_err = Gestalt(gestaltSystemVersionBugFix, &vMinorMinor);
						Assert(!gestalt_err);

						//encode into one quantity - 10.6.3 becomes 0x000A0603
						fields.m_osComboVersion = (vMajor << 16) | (vMinor << 8) | (vMinorMinor);

						if (CommandLine()->FindParm("-fakeleopard"))
						{
							// lie
							fields.m_osComboVersion = 0x000A0508;
						}
						
						if (fields.m_osComboVersion < 0x000A0508)
						{
							// no support below 10.5.8
							// we'll wind up with no valid renderers and give up
							selected = false;
						}
					}
					
					if (selected)
					{
						// gather more info from IOKit
						// cribbed from http://developer.apple.com/mac/library/samplecode/VideoHardwareInfo/listing3.html
						
						CFTypeRef typeCode;
						CFDataRef vendorID, deviceID, model;
						io_registry_entry_t dspPort;
							
						// Get the I/O Kit service port for the display
						dspPort = CGDisplayIOServicePort( cgid );

						// Get the information for the device
						// The vendor ID, device ID, and model are all available as properties of the hardware's I/O Kit service port
						
						vendorID	= (CFDataRef)IORegistryEntrySearchCFProperty(dspPort,kIOServicePlane,CFSTR("vendor-id"),	kCFAllocatorDefault,kIORegistryIterateRecursively | kIORegistryIterateParents);
						deviceID	= (CFDataRef)IORegistryEntrySearchCFProperty(dspPort,kIOServicePlane,CFSTR("device-id"),	kCFAllocatorDefault,kIORegistryIterateRecursively | kIORegistryIterateParents);
						model		= (CFDataRef)IORegistryEntrySearchCFProperty(dspPort,kIOServicePlane,CFSTR("model"),		kCFAllocatorDefault,kIORegistryIterateRecursively | kIORegistryIterateParents);
						
						// Send the appropriate data to the outputs checking to validate the data
						if(vendorID)
						{
							fields.m_pciVendorID = *((UInt32*)CFDataGetBytePtr(vendorID));
						}
						else
						{
							fields.m_pciVendorID = 0;
						}
						
						if(deviceID)
						{
							fields.m_pciDeviceID = *((UInt32*)CFDataGetBytePtr(deviceID));
						}
						else
						{
							fields.m_pciDeviceID = 0;
						}
						
						if(model)
						{
							int length = CFDataGetLength(model);
							char *data = (char*)CFDataGetBytePtr(model);
							Q_strncpy( fields.m_pciModelString, data, sizeof(fields.m_pciModelString) );
						}
						else
						{
							Q_strncpy( fields.m_pciModelString, "UnknownModel", sizeof(fields.m_pciModelString) );
						}
						

						// iterate through IOAccelerators til we find one that matches the vendorid and deviceid of this renderer (ugh!)
						// this provides the driver version string which can in turn be used to uniquely identify bad drivers and special case for them
						// first example to date - forcing vsync on 10.6.4 + NV
						
						{
							io_iterator_t	ioIterator		= (io_iterator_t)0;
							io_service_t	ioAccelerator;
							kern_return_t	ioResult		= 0;
							bool			ioDone			= false;
														
							ioResult = IOServiceGetMatchingServices( kIOMasterPortDefault, IOServiceMatching("IOAccelerator"), &ioIterator );
							if( ioResult == KERN_SUCCESS )
							{
								ioAccelerator = 0;

								while( ( !ioDone ) && ( ioAccelerator = IOIteratorNext( ioIterator ) )  )
								{
									io_service_t ioDevice;
									
									ioDevice = 0;
									ioResult = IORegistryEntryGetParentEntry( ioAccelerator, kIOServicePlane, &ioDevice);
									
									CFDataRef this_vendorID, this_deviceID;

									if(ioResult == KERN_SUCCESS)
									{
										this_vendorID	=	(CFDataRef)IORegistryEntryCreateCFProperty(ioDevice, CFSTR("vendor-id"), kCFAllocatorDefault, kNilOptions );
										this_deviceID	=	(CFDataRef)IORegistryEntryCreateCFProperty(ioDevice, CFSTR("device-id"), kCFAllocatorDefault, kNilOptions );
										
										if (this_vendorID && this_deviceID)	// null check..
										{
											// see if it matches. if so, do our business (get the extended version string), set ioDone, call it a day
											unsigned short this_vendorIDValue = *(unsigned short*)CFDataGetBytePtr(this_vendorID);
											unsigned short this_deviceIDValue = *(unsigned short*)CFDataGetBytePtr(this_deviceID);
											
											if ( (fields.m_pciVendorID == this_vendorIDValue) && (fields.m_pciDeviceID == this_deviceIDValue) )
											{
												// see if it matches. if so, do our business (get the extended version string), set ioDone, call it a day
												unsigned short* this_vendorIDBytes = (unsigned short*)CFDataGetBytePtr( this_vendorID );
												unsigned short* this_deviceIDBytes = (unsigned short*)CFDataGetBytePtr( this_deviceID );
												
												if (this_vendorIDBytes && this_deviceIDBytes)	// null check...
												{
													unsigned short this_vendorIDValue = *this_vendorIDBytes;
													unsigned short this_deviceIDValue = *this_deviceIDBytes;
													
													if ( (fields.m_pciVendorID == this_vendorIDValue) && (fields.m_pciDeviceID == this_deviceIDValue) )
													{
														// match, stop looking
														ioDone = true;
														
														// get extended info
														CFStringRef this_ioglName = (CFStringRef)IORegistryEntryCreateCFProperty( ioAccelerator, CFSTR("IOGLBundleName"), kCFAllocatorDefault, kNilOptions );
	
														NSString *bundlePath = [ NSString stringWithFormat:@"/System/Library/Extensions/%@.bundle", this_ioglName ];
														
														NSDictionary* this_driverDict = [ [NSBundle bundleWithPath: bundlePath] infoDictionary ];
														if (this_driverDict)
														{
															NSString* this_driverInfo = [ this_driverDict objectForKey:@"CFBundleGetInfoString" ];
															if ( this_driverInfo )
															{
																const char* theString = [ this_driverInfo UTF8String ];
																
																strncpy(fields.m_driverInfoString, theString, sizeof( fields.m_driverInfoString )  );
															}												
														}
														
														// [bundlePath release];
														
														CFRelease(this_ioglName);
													}
												}
	
												CFRelease(this_vendorID);
												CFRelease(this_deviceID);
											}
										}
									}
								}
							}

							IOObjectRelease(ioAccelerator);
							IOObjectRelease(ioIterator);
						}

						// Release vendorID, deviceID, and model as appropriate
						if(vendorID)
							CFRelease(vendorID);
						if(deviceID)
							CFRelease(deviceID);
						if(model)
							CFRelease(model);

						// generate shorthand bools
						switch( fields.m_pciVendorID )
						{
							case	0x1002:	//ATI
							{
								fields.m_ati = true;

								// http://www.pcidatabase.com/search.php?device_search_str=radeon&device_search.x=0&device_search.y=0&device_search=search+devices
								
								// Mac-relevant ATI R5xx PCI device ID's lie in this range: 0x7100 - 0x72FF
								// X1600, X1900, X1950
								if ( (fields.m_pciDeviceID >= 0x7100) && (fields.m_pciDeviceID <= 0x72ff) )
								{
									fields.m_atiR5xx = true;
								}

								// R6xx PCI device ID's lie in these ranges:
									// 0x94C1 - 0x9515 ... also 0x9581 - 0x9713
									// 2400HD, 2600HD, 3870, et al
								if	( 
										( (fields.m_pciDeviceID >= 0x94C1) && (fields.m_pciDeviceID <= 0x9515) )
									||	( (fields.m_pciDeviceID >= 0x9581) && (fields.m_pciDeviceID <= 0x9713) )
									)
								{
									fields.m_atiR6xx = true;
								}

								// R7xx PCI device ID's lie in: 0x9440 - 0x9460, also 9480-94b5.
								// why there is an HD5000 at 9462, I dunno.  Don't think that's an R8xx part.
								if	( 
										( (fields.m_pciDeviceID >= 0x9440) && (fields.m_pciDeviceID <= 0x9460) )
									||	( (fields.m_pciDeviceID >= 0x9480) && (fields.m_pciDeviceID <= 0x94B5) )
									)
								{
									fields.m_atiR7xx = true;
								}
								
								// R8xx: 0x6898-0x68BE
								if ( (fields.m_pciDeviceID >= 0x6898) && (fields.m_pciDeviceID <= 0x68Be) )
								{
									fields.m_atiR8xx = true;
								}

								#if 0
										// turned off, but we could use this for cross check.
										// we could also use the bit encoding of the renderer ID to ferret out a geberation clue.
										
										// string-scan for each generation
										// this could be a lot better if we got the precise PCI ID's used and/or cross-ref'd that against the driver name
										if (strstr("X1600", fields.m_pciModelString) || strstr("X1900", fields.m_pciModelString) || strstr("X1950", fields.m_pciModelString) )
										{
											fields.m_atiR5xx = true;
										}

										if (strstr("2600", fields.m_pciModelString) || strstr("3870", fields.m_pciModelString) || strstr("X2000", fields.m_pciModelString) )
										{
											fields.m_atiR6xx = true;
										}

										if (strstr("4670", fields.m_pciModelString) || strstr("4650", fields.m_pciModelString) || strstr("4850", fields.m_pciModelString)|| strstr("4870", fields.m_pciModelString) )
										{
											fields.m_atiR7xx = true;
										}
								#endif
							}
							break;
							
							case	0x8086:	//INTC
							{
								fields.m_intel = true;
								
								switch( fields.m_pciDeviceID )
								{
									case	0x27A6:	fields.m_intel95x = true;		break;	// GMA 950
									case	0x2A02:	fields.m_intel3100 = true;		break;	// X3100
									case	0x0166: fields.m_intelHD4000 = true;	break;	// HD4000
								}
							}
							break;
							
							case	0x10DE:	//NV
							{
								fields.m_nv = true;

								// G7x: 0x0391 0x393 0x0395 (7300/7600 GT)  0x009D (Quadro FX)
								if	( (fields.m_pciDeviceID == 0x0391) || (fields.m_pciDeviceID == 0x0393) || (fields.m_pciDeviceID == 0x0395) || (fields.m_pciDeviceID == 0x009D) )
								{
									fields.m_nvG7x = true;
								}
								
								// G8x: 0400-04ff, also 0x5E1 (GTX280) through 0x08FF
								if	(
										( (fields.m_pciDeviceID >= 0x0400) && (fields.m_pciDeviceID <= 0x04ff) )
									||	( (fields.m_pciDeviceID >= 0x05E1) && (fields.m_pciDeviceID <= 0x08ff) )
									)
								{
									fields.m_nvG8x = true;
								}

								if ( fields.m_pciDeviceID > 0x0900 )
								{
									fields.m_nvNewer = true;
								}
								
								// detect the specific revision of NV driver in 10.6.4 that caused all the grief
								if (strstr(fields.m_driverInfoString, "1.6.16.11 (19.5.8f01)"))
								{
									fields.m_badDriver1064NV = true;
								}
							}
							break;
						}						
					}
					
					if (selected)
					{
						// dupe check
						FOR_EACH_VEC( *m_renderers, i )
						{
							uint rendid = (*m_renderers)[i]->m_info.m_rendererID;
							
							if ( rendid == fields.m_rendererID )
							{
								// don't add to table, it's a dupe
								selected = false;
							}
						}
					}
					
					if (selected)
					{
						// criteria check
						if (fields.m_fullscreen==0)
							selected = false;
						if (fields.m_accelerated==0)
							selected = false;
						if (fields.m_windowed==0)
							selected = false;
					}

					Assert( fields.m_displayMask != 0 );
					
					if (selected)
					{
						// add to table
						// note this constructor makes a dummy context just long enough to query remaining fields in the m_info.
						GLMRendererInfo *newinfo = new GLMRendererInfo( &fields );
						m_renderers->AddToTail( newinfo );
					}
				}
				if (cgl_rend)
				{
					CGLDestroyRendererInfo( cgl_rend );
				}
			}
		}
	}
	
	// now sort the table.
	m_renderers->Sort( RendererInfoSortFunction );

	// then go back and ask each renderer to populate its display info table.
	FOR_EACH_VEC( *m_renderers, i )
	{
		(*m_renderers)[i]->PopulateDisplays();
	}
}

void	GLMDisplayDB::PopulateFakeAdapters( uint realRendererIndex )		// fake adapters = one real adapter times however many displays are on it
{
	// presumption is that renderers have been populated.
	Assert( GetRendererCount() > 0 );
	Assert( realRendererIndex < GetRendererCount() );
	
	m_fakeAdapters.RemoveAll();
	
	// for( int r = 0; r < GetRendererCount(); r++ )
	int r = realRendererIndex;
	{
		for( int d = 0; d < GetDisplayCount( r ); d++ )
		{
			GLMFakeAdapter temp;
			
			temp.m_rendererIndex = r;
			temp.m_displayIndex = d;
			
			m_fakeAdapters.AddToTail( temp );
		}
	}
}

void	GLMDisplayDB::Populate(void)
{
	this->PopulateRenderers();
	
	// passing in zero here, constrains the set of fake adapters (GL renderer + a display) to the ones using the highest ranked renderer.
	//FIXME introduce some kind of convar allowing selection of other GPU's in the system.
	
	int realRendererIndex = 0;

	if (CommandLine()->FindParm("-glmrenderer0"))
		realRendererIndex = 0;
	if (CommandLine()->FindParm("-glmrenderer1"))
		realRendererIndex = 1;
	if (CommandLine()->FindParm("-glmrenderer2"))
		realRendererIndex = 2;
	if (CommandLine()->FindParm("-glmrenderer3"))
		realRendererIndex = 3;
		
	if (realRendererIndex >= GetRendererCount())
	{
		// fall back to 0
		realRendererIndex = 0;
	}
	
	this->PopulateFakeAdapters( 0 );

	#if GLMDEBUG
		this->Dump();
	#endif
}
	


int		GLMDisplayDB::GetFakeAdapterCount( void )
{
	return m_fakeAdapters.Count();
}

bool	GLMDisplayDB::GetFakeAdapterInfo( int fakeAdapterIndex, int *rendererOut, int *displayOut, GLMRendererInfoFields *rendererInfoOut, GLMDisplayInfoFields *displayInfoOut )
{
	if (fakeAdapterIndex >= GetFakeAdapterCount() )
	{
		*rendererOut = 0;
		*displayOut = 0;
		return true;		// fail
	}

	*rendererOut = m_fakeAdapters[fakeAdapterIndex].m_rendererIndex;
	*displayOut = m_fakeAdapters[fakeAdapterIndex].m_displayIndex;

	bool rendResult = GetRendererInfo( *rendererOut, rendererInfoOut );
	bool dispResult = GetDisplayInfo( *rendererOut, *displayOut, displayInfoOut );
	
	return rendResult || dispResult;
}
	

int		GLMDisplayDB::GetRendererCount( void )
{
	return	m_renderers->Count();
}

bool	GLMDisplayDB::GetRendererInfo( int rendererIndex, GLMRendererInfoFields *infoOut )
{
	memset( infoOut, 0, sizeof( GLMRendererInfoFields ) );

	if (rendererIndex >= GetRendererCount())
		return true; // fail
	
	GLMRendererInfo *rendInfo = (*m_renderers)[rendererIndex];		
	*infoOut = rendInfo->m_info;

	return false;
}

int		GLMDisplayDB::GetDisplayCount( int rendererIndex )
{
	if (rendererIndex >= GetRendererCount())
		return 0; // fail
	
	GLMRendererInfo *rendInfo = (*m_renderers)[rendererIndex];
		
	return	rendInfo->m_displays->Count();
}

bool	GLMDisplayDB::GetDisplayInfo( int rendererIndex, int displayIndex, GLMDisplayInfoFields *infoOut )
{
	memset( infoOut, 0, sizeof( GLMDisplayInfoFields ) );
	
	if (rendererIndex >= GetRendererCount())
		return true; // fail
	
	if (displayIndex >= GetDisplayCount(rendererIndex))
		return true; // fail
	
	GLMDisplayInfo *displayInfo = (*(*m_renderers)[rendererIndex]->m_displays)[displayIndex];
	*infoOut = displayInfo->m_info;

	return false;
}

int		GLMDisplayDB::GetModeCount( int rendererIndex, int displayIndex )
{
	if (rendererIndex >= GetRendererCount())
		return 0; // fail
	
	if (displayIndex >= GetDisplayCount(rendererIndex))
		return 0; // fail
		
	GLMDisplayInfo *displayInfo = (*(*m_renderers)[rendererIndex]->m_displays)[displayIndex];

	return displayInfo->m_modes->Count();
}

bool	GLMDisplayDB::GetModeInfo( int rendererIndex, int displayIndex, int modeIndex, GLMDisplayModeInfoFields *infoOut )
{
	memset( infoOut, 0, sizeof( GLMDisplayModeInfoFields ) );
	
	if (rendererIndex >= GetRendererCount())
		return true; // fail
	
	if (displayIndex >= GetDisplayCount(rendererIndex))
		return true; // fail
	
	if (modeIndex >= GetModeCount(rendererIndex,displayIndex))
		return true; // fail
	
	if (modeIndex>=0)
	{
		GLMDisplayMode *displayModeInfo = (*(*(*m_renderers)[rendererIndex]->m_displays)[displayIndex]->m_modes)[ modeIndex ];
		*infoOut = displayModeInfo->m_info;
	}
	else
	{
		// passing modeIndex = -1 means "tell me about current mode"..

		GLMRendererInfo		*rendInfo = (*m_renderers)[ rendererIndex ];
		GLMDisplayInfo		*dispinfo = (*rendInfo ->m_displays)[displayIndex];	
		CGDirectDisplayID	cgid = dispinfo->m_info.m_cgDisplayID;
		
		CFDictionaryRef		curModeDict = CGDisplayCurrentMode( cgid );
		CFNumberRef			number;
		CFBooleanRef		boolean;
		CFArrayRef			modeList;
		CGDisplayErr		cgderr;
		
		// get the mode number from the mode dict (using system mode numbering, not our sorted numbering)
		if (curModeDict)
		{
			int modeIndex=0;
			number = (CFNumberRef)CFDictionaryGetValue(curModeDict, kCGDisplayMode);
			CFNumberGetValue(number, kCFNumberLongType, &modeIndex);

			// grab the width and height, I am unclear on whether this is the displayed FB width or the display device width.
			int screenWidth=0;
			int screenHeight=0;
			int refreshHz=0;
			
			number = (CFNumberRef)CFDictionaryGetValue(curModeDict, kCGDisplayWidth);
			CFNumberGetValue(number, kCFNumberLongType, &screenWidth);
			number = (CFNumberRef)CFDictionaryGetValue(curModeDict, kCGDisplayHeight);
			CFNumberGetValue(number, kCFNumberLongType, &screenHeight);
			number = (CFNumberRef)CFDictionaryGetValue(curModeDict, kCGDisplayRefreshRate);
			CFNumberGetValue(number, kCFNumberLongType, &refreshHz);
			
			GLMPRINTF(( "-D- GLMDisplayDB::GetModeInfo sees mode-index=%d, width=%d, height=%d on CGID %08x (display index %d on rendererindex %d)", 
				modeIndex,
				screenWidth,
				screenHeight,
				cgid,
				displayIndex,
				rendererIndex ));

			// now match
			int foundIndex = -1;
			FOR_EACH_VEC( (*dispinfo->m_modes), i )
			{
				GLMDisplayMode *mode = (*dispinfo->m_modes)[i];
				
				if (mode->m_info.m_modePixelWidth == screenWidth)
				{
					if (mode->m_info.m_modePixelHeight == screenHeight)
					{
						if (mode->m_info.m_modeRefreshHz == refreshHz)
						{
							foundIndex = i;
							*infoOut = mode->m_info;
							return false;
						}
					}
				}
			}
		}

		// if we get here, we could not find the mode
		memset( infoOut, 0, sizeof( *infoOut ) );
		return true; // fail
	}
	return false;
}


void	GLMDisplayDB::Dump( void )
{
	GLMPRINTF(("\n GLMDisplayDB @ %08x ",this ));

	FOR_EACH_VEC( *m_renderers, i )
	{
		(*m_renderers)[i]->Dump(i);
	}
}

//===============================================================================

GLMDisplayInfo::GLMDisplayInfo( CGDirectDisplayID displayID, CGOpenGLDisplayMask displayMask )
{	
	m_info.m_cgDisplayID			= displayID;
	m_info.m_glDisplayMask			= displayMask;
	
	// extract info about this display such as pixel width and height
	m_info.m_displayPixelWidth		= (uint)CGDisplayPixelsWide( m_info.m_cgDisplayID );
	m_info.m_displayPixelHeight		= (uint)CGDisplayPixelsHigh( m_info.m_cgDisplayID );

	m_modes = NULL;
}

GLMDisplayInfo::~GLMDisplayInfo( void )
{
	if (m_modes)
	{
		// delete all the new'd display modes
		FOR_EACH_VEC( *m_modes, i )
		{
			delete (*this->m_modes)[i];
		}
		delete m_modes;
		m_modes = NULL;
	}
}


extern "C" int DisplayModeSortFunction( GLMDisplayMode * const *A, GLMDisplayMode * const *B )
{
	int bigger = -1;
	int smaller = 1;	// adjust these for desired ordering

	// check refreshrate - higher should win
	if ( (*A)->m_info.m_modeRefreshHz > (*B)->m_info.m_modeRefreshHz )
	{	
		return bigger;
	}
	else if ( (*A)->m_info.m_modeRefreshHz < (*B)->m_info.m_modeRefreshHz )
	{
		return smaller;
	}

	// check area - larger mode should win
	int areaa = (*A)->m_info.m_modePixelWidth * (*A)->m_info.m_modePixelHeight;
	int areab = (*B)->m_info.m_modePixelWidth * (*B)->m_info.m_modePixelHeight;

	if ( areaa > areab )
	{	
		return bigger;
	}
	else if ( areaa < areab )
	{
		return smaller;
	}
	
	return 0;	// equal rank
}


void	GLMDisplayInfo::PopulateModes( void )
{
	Assert( !m_modes );
	m_modes = new CUtlVector< GLMDisplayMode* >;
	
	CFArrayRef		modeList;
//	CGDisplayErr	cgderr;
	CFDictionaryRef cgvidmode;
	CFNumberRef		number;
	CFBooleanRef	boolean;
	
	modeList = CGDisplayAvailableModes( m_info.m_cgDisplayID );
	if ( modeList != NULL )
	{
		//  examine each mode
		CFIndex count = CFArrayGetCount( modeList );
		
		for (CFIndex i = 0; i < count; i++) 
		{
			long modeHeight = 0, modeWidth = 0;
			long depth = 0;
			long refreshrate = 0;
			Boolean usable, stretched = false;
			
			// grab the mode dictionary
			cgvidmode = (CFDictionaryRef)CFArrayGetValueAtIndex( modeList, i);
			
			// grab mode params we need
			number = (CFNumberRef)CFDictionaryGetValue(cgvidmode, kCGDisplayBitsPerPixel);
			CFNumberGetValue(number, kCFNumberLongType, &depth);
			
			boolean = (CFBooleanRef)CFDictionaryGetValue(cgvidmode, kCGDisplayModeUsableForDesktopGUI) ;
			usable = CFBooleanGetValue(boolean);
			
			boolean = (CFBooleanRef)CFDictionaryGetValue(cgvidmode, kCGDisplayModeIsStretched);
			if (NULL != boolean) 
			{
				stretched = CFBooleanGetValue(boolean);
			}
			
			if ( usable && (!stretched) && (depth==32) )
			{
				// we're going to log this mode to the mode table.
				
				// get height of mode
				number = (CFNumberRef)CFDictionaryGetValue( cgvidmode, kCGDisplayHeight );
				CFNumberGetValue(number, kCFNumberLongType, &modeHeight);
				
				// get width of mode
				number = (CFNumberRef)CFDictionaryGetValue( cgvidmode, kCGDisplayWidth );
				CFNumberGetValue(number, kCFNumberLongType, &modeWidth);
				
				// get refresh rate of mode
				number = (CFNumberRef)CFDictionaryGetValue( cgvidmode, kCGDisplayRefreshRate ); 
				double flrefreshrate = 0.0f;
				CFNumberGetValue( number, kCFNumberDoubleType, &flrefreshrate );
				refreshrate = (int)flrefreshrate;

				// exclude silly small modes
				if ( (modeHeight >= 384) && (modeWidth >= 512) )
				{
					GLMDisplayMode *newmode = new GLMDisplayMode( modeWidth, modeHeight, refreshrate );
					m_modes->AddToTail( newmode );
				}
			}
		}
	}
	
	// now sort the modes
	// primary key is refresh rate
	// secondary key is area

	m_modes->Sort( DisplayModeSortFunction );
}


void	GLMDisplayInfo::Dump( int which )
{
	GLMPRINTF(("\n         #%d: GLMDisplayInfo @ %08x, cg-id=%08x  display-mask=%08x  pixwidth=%d  pixheight=%d", which, (int)this, m_info.m_cgDisplayID, m_info.m_glDisplayMask, m_info.m_displayPixelWidth,  m_info.m_displayPixelHeight ));

	FOR_EACH_VEC( *m_modes, i )
	{
		(*m_modes)[i]->Dump(i);
	}
}