summaryrefslogtreecommitdiff
path: root/appframework
diff options
context:
space:
mode:
Diffstat (limited to 'appframework')
-rw-r--r--appframework/AppSystemGroup.cpp573
-rw-r--r--appframework/VguiMatSysApp.cpp308
-rw-r--r--appframework/WinApp.cpp248
-rw-r--r--appframework/appframework.vpc53
-rw-r--r--appframework/glmdisplaydb_linuxwin.inl630
-rw-r--r--appframework/glmrendererinfo_osx.mm1583
-rw-r--r--appframework/posixapp.cpp182
-rw-r--r--appframework/sdlmgr.cpp2078
8 files changed, 5655 insertions, 0 deletions
diff --git a/appframework/AppSystemGroup.cpp b/appframework/AppSystemGroup.cpp
new file mode 100644
index 0000000..196bd80
--- /dev/null
+++ b/appframework/AppSystemGroup.cpp
@@ -0,0 +1,573 @@
+//========= Copyright 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 "appframework/IAppSystemGroup.h"
+#include "appframework/IAppSystem.h"
+#include "interface.h"
+#include "filesystem.h"
+#include "filesystem_init.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// constructor, destructor
+//-----------------------------------------------------------------------------
+//extern ILoggingListener *g_pDefaultLoggingListener;
+
+
+//-----------------------------------------------------------------------------
+// constructor, destructor
+//-----------------------------------------------------------------------------
+CAppSystemGroup::CAppSystemGroup( CAppSystemGroup *pAppSystemParent ) : m_SystemDict(false, 0, 16)
+{
+ m_pParentAppSystem = pAppSystemParent;
+}
+
+
+//-----------------------------------------------------------------------------
+// Actually loads a DLL
+//-----------------------------------------------------------------------------
+CSysModule *CAppSystemGroup::LoadModuleDLL( const char *pDLLName )
+{
+ return Sys_LoadModule( pDLLName );
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods to load + unload DLLs
+//-----------------------------------------------------------------------------
+AppModule_t CAppSystemGroup::LoadModule( const char *pDLLName )
+{
+ // Remove the extension when creating the name.
+ int nLen = Q_strlen( pDLLName ) + 1;
+ char *pModuleName = (char*)stackalloc( nLen );
+ Q_StripExtension( pDLLName, pModuleName, nLen );
+
+ // See if we already loaded it...
+ for ( int i = m_Modules.Count(); --i >= 0; )
+ {
+ if ( m_Modules[i].m_pModuleName )
+ {
+ if ( !Q_stricmp( pModuleName, m_Modules[i].m_pModuleName ) )
+ return i;
+ }
+ }
+
+ CSysModule *pSysModule = LoadModuleDLL( pDLLName );
+ if (!pSysModule)
+ {
+ Warning("AppFramework : Unable to load module %s!\n", pDLLName );
+ return APP_MODULE_INVALID;
+ }
+
+ int nIndex = m_Modules.AddToTail();
+ m_Modules[nIndex].m_pModule = pSysModule;
+ m_Modules[nIndex].m_Factory = 0;
+ m_Modules[nIndex].m_pModuleName = (char*)malloc( nLen );
+ Q_strncpy( m_Modules[nIndex].m_pModuleName, pModuleName, nLen );
+
+ return nIndex;
+}
+
+AppModule_t CAppSystemGroup::LoadModule( CreateInterfaceFn factory )
+{
+ if (!factory)
+ {
+ Warning("AppFramework : Unable to load module %p!\n", factory );
+ return APP_MODULE_INVALID;
+ }
+
+ // See if we already loaded it...
+ for ( int i = m_Modules.Count(); --i >= 0; )
+ {
+ if ( m_Modules[i].m_Factory )
+ {
+ if ( m_Modules[i].m_Factory == factory )
+ return i;
+ }
+ }
+
+ int nIndex = m_Modules.AddToTail();
+ m_Modules[nIndex].m_pModule = NULL;
+ m_Modules[nIndex].m_Factory = factory;
+ m_Modules[nIndex].m_pModuleName = NULL;
+ return nIndex;
+}
+
+void CAppSystemGroup::UnloadAllModules()
+{
+ // NOTE: Iterate in reverse order so they are unloaded in opposite order
+ // from loading
+ for (int i = m_Modules.Count(); --i >= 0; )
+ {
+ if ( m_Modules[i].m_pModule )
+ {
+ Sys_UnloadModule( m_Modules[i].m_pModule );
+ }
+ if ( m_Modules[i].m_pModuleName )
+ {
+ free( m_Modules[i].m_pModuleName );
+ }
+ }
+ m_Modules.RemoveAll();
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods to add/remove various global singleton systems
+//-----------------------------------------------------------------------------
+IAppSystem *CAppSystemGroup::AddSystem( AppModule_t module, const char *pInterfaceName )
+{
+ if (module == APP_MODULE_INVALID)
+ return NULL;
+
+ Assert( (module >= 0) && (module < m_Modules.Count()) );
+ CreateInterfaceFn pFactory = m_Modules[module].m_pModule ? Sys_GetFactory( m_Modules[module].m_pModule ) : m_Modules[module].m_Factory;
+
+ int retval;
+ void *pSystem = pFactory( pInterfaceName, &retval );
+ if ((retval != IFACE_OK) || (!pSystem))
+ {
+ Warning("AppFramework : Unable to create system %s!\n", pInterfaceName );
+ return NULL;
+ }
+
+ IAppSystem *pAppSystem = static_cast<IAppSystem*>(pSystem);
+
+ int sysIndex = m_Systems.AddToTail( pAppSystem );
+
+ // Inserting into the dict will help us do named lookup later
+ MEM_ALLOC_CREDIT();
+ m_SystemDict.Insert( pInterfaceName, sysIndex );
+ return pAppSystem;
+}
+
+static char const *g_StageLookup[] =
+{
+ "CREATION",
+ "CONNECTION",
+ "PREINITIALIZATION",
+ "INITIALIZATION",
+ "SHUTDOWN",
+ "POSTSHUTDOWN",
+ "DISCONNECTION",
+ "DESTRUCTION",
+ "NONE",
+};
+
+void CAppSystemGroup::ReportStartupFailure( int nErrorStage, int nSysIndex )
+{
+ char const *pszStageDesc = "Unknown";
+ if ( nErrorStage >= 0 && nErrorStage < ARRAYSIZE( g_StageLookup ) )
+ {
+ pszStageDesc = g_StageLookup[ nErrorStage ];
+ }
+
+ char const *pszSystemName = "(Unknown)";
+ for ( int i = m_SystemDict.First(); i != m_SystemDict.InvalidIndex(); i = m_SystemDict.Next( i ) )
+ {
+ if ( m_SystemDict[ i ] != nSysIndex )
+ continue;
+
+ pszSystemName = m_SystemDict.GetElementName( i );
+ break;
+ }
+
+ // Walk the dictionary
+ Warning( "System (%s) failed during stage %s\n", pszSystemName, pszStageDesc );
+}
+
+void CAppSystemGroup::AddSystem( IAppSystem *pAppSystem, const char *pInterfaceName )
+{
+ if ( !pAppSystem )
+ return;
+
+ int sysIndex = m_Systems.AddToTail( pAppSystem );
+
+ // Inserting into the dict will help us do named lookup later
+ MEM_ALLOC_CREDIT();
+ m_SystemDict.Insert( pInterfaceName, sysIndex );
+}
+
+void CAppSystemGroup::RemoveAllSystems()
+{
+ // NOTE: There's no deallcation here since we don't really know
+ // how the allocation has happened. We could add a deallocation method
+ // to the code in interface.h; although when the modules are unloaded
+ // the deallocation will happen anyways
+ m_Systems.RemoveAll();
+ m_SystemDict.RemoveAll();
+}
+
+
+//-----------------------------------------------------------------------------
+// Simpler method of doing the LoadModule/AddSystem thing.
+//-----------------------------------------------------------------------------
+bool CAppSystemGroup::AddSystems( AppSystemInfo_t *pSystemList )
+{
+ while ( pSystemList->m_pModuleName[0] )
+ {
+ AppModule_t module = LoadModule( pSystemList->m_pModuleName );
+ IAppSystem *pSystem = AddSystem( module, pSystemList->m_pInterfaceName );
+ if ( !pSystem )
+ {
+ Warning( "Unable to load interface %s from %s\n", pSystemList->m_pInterfaceName, pSystemList->m_pModuleName );
+ return false;
+ }
+ ++pSystemList;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods to find various global singleton systems
+//-----------------------------------------------------------------------------
+void *CAppSystemGroup::FindSystem( const char *pSystemName )
+{
+ unsigned short i = m_SystemDict.Find( pSystemName );
+ if (i != m_SystemDict.InvalidIndex())
+ return m_Systems[m_SystemDict[i]];
+
+ // If it's not an interface we know about, it could be an older
+ // version of an interface, or maybe something implemented by
+ // one of the instantiated interfaces...
+
+ // QUESTION: What order should we iterate this in?
+ // It controls who wins if multiple ones implement the same interface
+ for ( i = 0; i < m_Systems.Count(); ++i )
+ {
+ void *pInterface = m_Systems[i]->QueryInterface( pSystemName );
+ if (pInterface)
+ return pInterface;
+ }
+
+ if ( m_pParentAppSystem )
+ {
+ void* pInterface = m_pParentAppSystem->FindSystem( pSystemName );
+ if ( pInterface )
+ return pInterface;
+ }
+
+ // No dice..
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets at the parent appsystem group
+//-----------------------------------------------------------------------------
+CAppSystemGroup *CAppSystemGroup::GetParent()
+{
+ return m_pParentAppSystem;
+}
+
+
+//-----------------------------------------------------------------------------
+// Method to connect/disconnect all systems
+//-----------------------------------------------------------------------------
+bool CAppSystemGroup::ConnectSystems()
+{
+ for (int i = 0; i < m_Systems.Count(); ++i )
+ {
+ IAppSystem *sys = m_Systems[i];
+
+ if (!sys->Connect( GetFactory() ))
+ {
+ ReportStartupFailure( CONNECTION, i );
+ return false;
+ }
+ }
+ return true;
+}
+
+void CAppSystemGroup::DisconnectSystems()
+{
+ // Disconnect in reverse order of connection
+ for (int i = m_Systems.Count(); --i >= 0; )
+ {
+ m_Systems[i]->Disconnect();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Method to initialize/shutdown all systems
+//-----------------------------------------------------------------------------
+InitReturnVal_t CAppSystemGroup::InitSystems()
+{
+ for (int i = 0; i < m_Systems.Count(); ++i )
+ {
+ InitReturnVal_t nRetVal = m_Systems[i]->Init();
+ if ( nRetVal != INIT_OK )
+ {
+ ReportStartupFailure( INITIALIZATION, i );
+ return nRetVal;
+ }
+ }
+ return INIT_OK;
+}
+
+void CAppSystemGroup::ShutdownSystems()
+{
+ // Shutdown in reverse order of initialization
+ for (int i = m_Systems.Count(); --i >= 0; )
+ {
+ m_Systems[i]->Shutdown();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the stage at which the app system group ran into an error
+//-----------------------------------------------------------------------------
+CAppSystemGroup::AppSystemGroupStage_t CAppSystemGroup::GetErrorStage() const
+{
+ return m_nErrorStage;
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets at a factory that works just like FindSystem
+//-----------------------------------------------------------------------------
+// This function is used to make this system appear to the outside world to
+// function exactly like the currently existing factory system
+CAppSystemGroup *s_pCurrentAppSystem;
+void *AppSystemCreateInterfaceFn(const char *pName, int *pReturnCode)
+{
+ void *pInterface = s_pCurrentAppSystem->FindSystem( pName );
+ if ( pReturnCode )
+ {
+ *pReturnCode = pInterface ? IFACE_OK : IFACE_FAILED;
+ }
+ return pInterface;
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets at a class factory for the topmost appsystem group in an appsystem stack
+//-----------------------------------------------------------------------------
+CreateInterfaceFn CAppSystemGroup::GetFactory()
+{
+ return AppSystemCreateInterfaceFn;
+}
+
+
+//-----------------------------------------------------------------------------
+// Main application loop
+//-----------------------------------------------------------------------------
+int CAppSystemGroup::Run()
+{
+ // The factory now uses this app system group
+ s_pCurrentAppSystem = this;
+
+ // Load, connect, init
+ int nRetVal = OnStartup();
+ if ( m_nErrorStage != NONE )
+ return nRetVal;
+
+ // Main loop implemented by the application
+ // FIXME: HACK workaround to avoid vgui porting
+ nRetVal = Main();
+
+ // Shutdown, disconnect, unload
+ OnShutdown();
+
+ // The factory now uses the parent's app system group
+ s_pCurrentAppSystem = GetParent();
+
+ return nRetVal;
+}
+
+
+//-----------------------------------------------------------------------------
+// Virtual methods for override
+//-----------------------------------------------------------------------------
+int CAppSystemGroup::Startup()
+{
+ return OnStartup();
+}
+
+void CAppSystemGroup::Shutdown()
+{
+ return OnShutdown();
+}
+
+
+//-----------------------------------------------------------------------------
+// Use this version in cases where you can't control the main loop and
+// expect to be ticked
+//-----------------------------------------------------------------------------
+int CAppSystemGroup::OnStartup()
+{
+ // The factory now uses this app system group
+ s_pCurrentAppSystem = this;
+
+ m_nErrorStage = NONE;
+
+ // Call an installed application creation function
+ if ( !Create() )
+ {
+ m_nErrorStage = CREATION;
+ return -1;
+ }
+
+ // Let all systems know about each other
+ if ( !ConnectSystems() )
+ {
+ m_nErrorStage = CONNECTION;
+ return -1;
+ }
+
+ // Allow the application to do some work before init
+ if ( !PreInit() )
+ {
+ m_nErrorStage = PREINITIALIZATION;
+ return -1;
+ }
+
+ // Call Init on all App Systems
+ int nRetVal = InitSystems();
+ if ( nRetVal != INIT_OK )
+ {
+ m_nErrorStage = INITIALIZATION;
+ return -1;
+ }
+
+ return nRetVal;
+}
+
+void CAppSystemGroup::OnShutdown()
+{
+ // The factory now uses this app system group
+ s_pCurrentAppSystem = this;
+
+ switch( m_nErrorStage )
+ {
+ case NONE:
+ break;
+
+ case PREINITIALIZATION:
+ case INITIALIZATION:
+ goto disconnect;
+
+ case CREATION:
+ case CONNECTION:
+ goto destroy;
+ }
+
+ // Cal Shutdown on all App Systems
+ ShutdownSystems();
+
+ // Allow the application to do some work after shutdown
+ PostShutdown();
+
+disconnect:
+ // Systems should disconnect from each other
+ DisconnectSystems();
+
+destroy:
+ // Unload all DLLs loaded in the AppCreate block
+ RemoveAllSystems();
+
+ // Have to do this because the logging listeners & response policies may live in modules which are being unloaded
+ // @TODO: this seems like a bad legacy practice... app systems should unload their spew handlers gracefully.
+// LoggingSystem_ResetCurrentLoggingState();
+// Assert( g_pDefaultLoggingListener != NULL );
+// LoggingSystem_RegisterLoggingListener( g_pDefaultLoggingListener );
+
+ UnloadAllModules();
+
+ // Call an installed application destroy function
+ Destroy();
+}
+
+
+
+//-----------------------------------------------------------------------------
+//
+// This class represents a group of app systems that are loaded through steam
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CSteamAppSystemGroup::CSteamAppSystemGroup( IFileSystem *pFileSystem, CAppSystemGroup *pAppSystemParent )
+{
+ m_pFileSystem = pFileSystem;
+ m_pGameInfoPath[0] = 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Used by CSteamApplication to set up necessary pointers if we can't do it in the constructor
+//-----------------------------------------------------------------------------
+void CSteamAppSystemGroup::Setup( IFileSystem *pFileSystem, CAppSystemGroup *pParentAppSystem )
+{
+ m_pFileSystem = pFileSystem;
+ m_pParentAppSystem = pParentAppSystem;
+}
+
+
+//-----------------------------------------------------------------------------
+// Loads the module from Steam
+//-----------------------------------------------------------------------------
+CSysModule *CSteamAppSystemGroup::LoadModuleDLL( const char *pDLLName )
+{
+ return m_pFileSystem->LoadModule( pDLLName );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the game info path
+//-----------------------------------------------------------------------------
+const char *CSteamAppSystemGroup::GetGameInfoPath() const
+{
+ return m_pGameInfoPath;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets up the search paths
+//-----------------------------------------------------------------------------
+bool CSteamAppSystemGroup::SetupSearchPaths( const char *pStartingDir, bool bOnlyUseStartingDir, bool bIsTool )
+{
+ CFSSteamSetupInfo steamInfo;
+ steamInfo.m_pDirectoryName = pStartingDir;
+ steamInfo.m_bOnlyUseDirectoryName = bOnlyUseStartingDir;
+ steamInfo.m_bToolsMode = bIsTool;
+ steamInfo.m_bSetSteamDLLPath = true;
+ steamInfo.m_bSteam = m_pFileSystem->IsSteam();
+ if ( FileSystem_SetupSteamEnvironment( steamInfo ) != FS_OK )
+ return false;
+
+ CFSMountContentInfo fsInfo;
+ fsInfo.m_pFileSystem = m_pFileSystem;
+ fsInfo.m_bToolsMode = bIsTool;
+ fsInfo.m_pDirectoryName = steamInfo.m_GameInfoPath;
+
+ if ( FileSystem_MountContent( fsInfo ) != FS_OK )
+ return false;
+
+ // Finally, load the search paths for the "GAME" path.
+ CFSSearchPathsInit searchPathsInit;
+ searchPathsInit.m_pDirectoryName = steamInfo.m_GameInfoPath;
+ searchPathsInit.m_pFileSystem = fsInfo.m_pFileSystem;
+ if ( FileSystem_LoadSearchPaths( searchPathsInit ) != FS_OK )
+ return false;
+
+ FileSystem_AddSearchPath_Platform( fsInfo.m_pFileSystem, steamInfo.m_GameInfoPath );
+ Q_strncpy( m_pGameInfoPath, steamInfo.m_GameInfoPath, sizeof(m_pGameInfoPath) );
+ return true;
+}
diff --git a/appframework/VguiMatSysApp.cpp b/appframework/VguiMatSysApp.cpp
new file mode 100644
index 0000000..90cf782
--- /dev/null
+++ b/appframework/VguiMatSysApp.cpp
@@ -0,0 +1,308 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// The copyright to the contents herein is the property of Valve, L.L.C.
+// The contents may be used and/or copied only with the written permission of
+// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
+// the agreement/contract under which the contents have been supplied.
+//
+//=============================================================================
+#ifdef _WIN32
+
+#if defined( _WIN32 ) && !defined( _X360 )
+#include <windows.h>
+#endif
+#include "appframework/vguimatsysapp.h"
+#include "filesystem.h"
+#include "materialsystem/imaterialsystem.h"
+#include "vgui/ivgui.h"
+#include "vgui/ISurface.h"
+#include "vgui_controls/controls.h"
+#include "vgui/ischeme.h"
+#include "vgui/ilocalize.h"
+#include "tier0/dbg.h"
+#include "tier0/icommandline.h"
+#include "materialsystem/materialsystem_config.h"
+#include "filesystem_init.h"
+#include "VGuiMatSurface/IMatSystemSurface.h"
+#include "inputsystem/iinputsystem.h"
+#include "tier3/tier3.h"
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CVguiMatSysApp::CVguiMatSysApp()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Create all singleton systems
+//-----------------------------------------------------------------------------
+bool CVguiMatSysApp::Create()
+{
+ AppSystemInfo_t appSystems[] =
+ {
+ { "inputsystem.dll", INPUTSYSTEM_INTERFACE_VERSION },
+ { "materialsystem.dll", MATERIAL_SYSTEM_INTERFACE_VERSION },
+
+ // NOTE: This has to occur before vgui2.dll so it replaces vgui2's surface implementation
+ { "vguimatsurface.dll", VGUI_SURFACE_INTERFACE_VERSION },
+ { "vgui2.dll", VGUI_IVGUI_INTERFACE_VERSION },
+
+ // Required to terminate the list
+ { "", "" }
+ };
+
+ if ( !AddSystems( appSystems ) )
+ return false;
+
+ IMaterialSystem *pMaterialSystem = (IMaterialSystem*)FindSystem( MATERIAL_SYSTEM_INTERFACE_VERSION );
+
+ if ( !pMaterialSystem )
+ {
+ Warning( "CVguiMatSysApp::Create: Unable to connect to necessary interface!\n" );
+ return false;
+ }
+
+ pMaterialSystem->SetShaderAPI( "shaderapidx9.dll" );
+ return true;
+}
+
+void CVguiMatSysApp::Destroy()
+{
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Window management
+//-----------------------------------------------------------------------------
+void*CVguiMatSysApp::CreateAppWindow( char const *pTitle, bool bWindowed, int w, int h )
+{
+ WNDCLASSEX wc;
+ memset( &wc, 0, sizeof( wc ) );
+ wc.cbSize = sizeof( wc );
+ wc.style = CS_OWNDC | CS_DBLCLKS;
+ wc.lpfnWndProc = DefWindowProc;
+ wc.hInstance = (HINSTANCE)GetAppInstance();
+ wc.lpszClassName = "Valve001";
+ wc.hIcon = NULL; //LoadIcon( s_HInstance, MAKEINTRESOURCE( IDI_LAUNCHER ) );
+ wc.hIconSm = wc.hIcon;
+
+ RegisterClassEx( &wc );
+
+ // Note, it's hidden
+ DWORD style = WS_POPUP | WS_CLIPSIBLINGS;
+
+ if ( bWindowed )
+ {
+ // Give it a frame
+ style |= WS_OVERLAPPEDWINDOW;
+ style &= ~WS_THICKFRAME;
+ }
+
+ // Never a max box
+ style &= ~WS_MAXIMIZEBOX;
+
+ RECT windowRect;
+ windowRect.top = 0;
+ windowRect.left = 0;
+ windowRect.right = w;
+ windowRect.bottom = h;
+
+ // Compute rect needed for that size client area based on window style
+ AdjustWindowRectEx(&windowRect, style, FALSE, 0);
+
+ // Create the window
+ void *hWnd = CreateWindow( wc.lpszClassName, pTitle, style, 0, 0,
+ windowRect.right - windowRect.left, windowRect.bottom - windowRect.top,
+ NULL, NULL, (HINSTANCE)GetAppInstance(), NULL );
+
+ if (!hWnd)
+ return NULL;
+
+ int CenterX, CenterY;
+
+ CenterX = (GetSystemMetrics(SM_CXSCREEN) - w) / 2;
+ CenterY = (GetSystemMetrics(SM_CYSCREEN) - h) / 2;
+ CenterX = (CenterX < 0) ? 0: CenterX;
+ CenterY = (CenterY < 0) ? 0: CenterY;
+
+ // In VCR modes, keep it in the upper left so mouse coordinates are always relative to the window.
+ SetWindowPos( (HWND)hWnd, NULL, CenterX, CenterY, 0, 0,
+ SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME);
+
+ return hWnd;
+}
+
+
+//-----------------------------------------------------------------------------
+// Pump messages
+//-----------------------------------------------------------------------------
+void CVguiMatSysApp::AppPumpMessages()
+{
+ g_pInputSystem->PollInputState();
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets up the game path
+//-----------------------------------------------------------------------------
+bool CVguiMatSysApp::SetupSearchPaths( const char *pStartingDir, bool bOnlyUseStartingDir, bool bIsTool )
+{
+ if ( !BaseClass::SetupSearchPaths( pStartingDir, bOnlyUseStartingDir, bIsTool ) )
+ return false;
+
+ g_pFullFileSystem->AddSearchPath( GetGameInfoPath(), "SKIN", PATH_ADD_TO_HEAD );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Init, shutdown
+//-----------------------------------------------------------------------------
+bool CVguiMatSysApp::PreInit( )
+{
+ if ( !BaseClass::PreInit() )
+ return false;
+
+ if ( !g_pFullFileSystem || !g_pMaterialSystem || !g_pInputSystem || !g_pMatSystemSurface )
+ {
+ Warning( "CVguiMatSysApp::PreInit: Unable to connect to necessary interface!\n" );
+ return false;
+ }
+
+ // Add paths...
+ if ( !SetupSearchPaths( NULL, false, true ) )
+ return false;
+
+ const char *pArg;
+ int iWidth = 1024;
+ int iHeight = 768;
+ bool bWindowed = (CommandLine()->CheckParm( "-fullscreen" ) == NULL);
+ if (CommandLine()->CheckParm( "-width", &pArg ))
+ {
+ iWidth = atoi( pArg );
+ }
+ if (CommandLine()->CheckParm( "-height", &pArg ))
+ {
+ iHeight = atoi( pArg );
+ }
+
+ m_nWidth = iWidth;
+ m_nHeight = iHeight;
+ m_HWnd = CreateAppWindow( GetAppName(), bWindowed, iWidth, iHeight );
+ if ( !m_HWnd )
+ return false;
+
+ g_pInputSystem->AttachToWindow( m_HWnd );
+ g_pMatSystemSurface->AttachToWindow( m_HWnd );
+
+ // NOTE: If we specifically wanted to use a particular shader DLL, we set it here...
+ //m_pMaterialSystem->SetShaderAPI( "shaderapidx8" );
+
+ // Get the adapter from the command line....
+ const char *pAdapterString;
+ int adapter = 0;
+ if (CommandLine()->CheckParm( "-adapter", &pAdapterString ))
+ {
+ adapter = atoi( pAdapterString );
+ }
+
+ int adapterFlags = 0;
+ if ( CommandLine()->CheckParm( "-ref" ) )
+ {
+ adapterFlags |= MATERIAL_INIT_REFERENCE_RASTERIZER;
+ }
+ if ( AppUsesReadPixels() )
+ {
+ adapterFlags |= MATERIAL_INIT_ALLOCATE_FULLSCREEN_TEXTURE;
+ }
+
+ g_pMaterialSystem->SetAdapter( adapter, adapterFlags );
+
+ return true;
+}
+
+void CVguiMatSysApp::PostShutdown()
+{
+ if ( g_pMatSystemSurface && g_pInputSystem )
+ {
+ g_pMatSystemSurface->AttachToWindow( NULL );
+ g_pInputSystem->DetachFromWindow( );
+ }
+
+ BaseClass::PostShutdown();
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the window size
+//-----------------------------------------------------------------------------
+int CVguiMatSysApp::GetWindowWidth() const
+{
+ return m_nWidth;
+}
+
+int CVguiMatSysApp::GetWindowHeight() const
+{
+ return m_nHeight;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the window
+//-----------------------------------------------------------------------------
+void* CVguiMatSysApp::GetAppWindow()
+{
+ return m_HWnd;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the video mode
+//-----------------------------------------------------------------------------
+bool CVguiMatSysApp::SetVideoMode( )
+{
+ MaterialSystem_Config_t config;
+ if ( CommandLine()->CheckParm( "-fullscreen" ) )
+ {
+ config.SetFlag( MATSYS_VIDCFG_FLAGS_WINDOWED, false );
+ }
+ else
+ {
+ config.SetFlag( MATSYS_VIDCFG_FLAGS_WINDOWED, true );
+ }
+
+ if ( CommandLine()->CheckParm( "-resizing" ) )
+ {
+ config.SetFlag( MATSYS_VIDCFG_FLAGS_RESIZING, true );
+ }
+
+ if ( CommandLine()->CheckParm( "-mat_vsync" ) )
+ {
+ config.SetFlag( MATSYS_VIDCFG_FLAGS_NO_WAIT_FOR_VSYNC, false );
+ }
+
+ config.m_nAASamples = CommandLine()->ParmValue( "-mat_antialias", 1 );
+ config.m_nAAQuality = CommandLine()->ParmValue( "-mat_aaquality", 0 );
+
+ config.m_VideoMode.m_Width = config.m_VideoMode.m_Height = 0;
+ config.m_VideoMode.m_Format = IMAGE_FORMAT_BGRX8888;
+ config.m_VideoMode.m_RefreshRate = 0;
+ config.SetFlag( MATSYS_VIDCFG_FLAGS_STENCIL, true );
+
+ bool modeSet = g_pMaterialSystem->SetMode( m_HWnd, config );
+ if (!modeSet)
+ {
+ Error( "Unable to set mode\n" );
+ return false;
+ }
+
+ g_pMaterialSystem->OverrideConfig( config, false );
+ return true;
+}
+
+#endif // _WIN32
+
diff --git a/appframework/WinApp.cpp b/appframework/WinApp.cpp
new file mode 100644
index 0000000..937a9f5
--- /dev/null
+++ b/appframework/WinApp.cpp
@@ -0,0 +1,248 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: An application framework
+//
+//=============================================================================//
+
+#ifdef POSIX
+#error
+#else
+#if defined( _WIN32 ) && !defined( _X360 )
+#include <windows.h>
+#endif
+#include "appframework/appframework.h"
+#include "tier0/dbg.h"
+#include "tier0/icommandline.h"
+#include "interface.h"
+#include "filesystem.h"
+#include "appframework/iappsystemgroup.h"
+#include "filesystem_init.h"
+#include "vstdlib/cvar.h"
+#include "xbox/xbox_console.h"
+
+// NOTE: This has to be the last file included!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Globals...
+//-----------------------------------------------------------------------------
+HINSTANCE s_HInstance;
+
+//static CSimpleWindowsLoggingListener s_SimpleWindowsLoggingListener;
+//static CSimpleLoggingListener s_SimpleLoggingListener;
+//ILoggingListener *g_pDefaultLoggingListener = &s_SimpleLoggingListener;
+
+//-----------------------------------------------------------------------------
+// HACK: Since I don't want to refit vgui yet...
+//-----------------------------------------------------------------------------
+void *GetAppInstance()
+{
+ return s_HInstance;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the application instance, should only be used if you're not calling AppMain.
+//-----------------------------------------------------------------------------
+void SetAppInstance( void* hInstance )
+{
+ s_HInstance = (HINSTANCE)hInstance;
+}
+
+//-----------------------------------------------------------------------------
+// Specific 360 environment setup.
+//-----------------------------------------------------------------------------
+#if defined( _X360 )
+bool SetupEnvironment360()
+{
+ CommandLine()->CreateCmdLine( GetCommandLine() );
+
+ if ( !CommandLine()->FindParm( "-game" ) && !CommandLine()->FindParm( "-vproject" ) )
+ {
+ // add the default game name due to lack of vproject environment
+ CommandLine()->AppendParm( "-game", "hl2" );
+ }
+
+ // success
+ return true;
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Version of AppMain used by windows applications
+//-----------------------------------------------------------------------------
+int AppMain( void* hInstance, void* hPrevInstance, const char* lpCmdLine, int nCmdShow, CAppSystemGroup *pAppSystemGroup )
+{
+ Assert( pAppSystemGroup );
+
+// g_pDefaultLoggingListener = &s_SimpleWindowsLoggingListener;
+ s_HInstance = (HINSTANCE)hInstance;
+#if !defined( _X360 )
+ CommandLine()->CreateCmdLine( ::GetCommandLine() );
+#else
+ SetupEnvironment360();
+#endif
+
+ return pAppSystemGroup->Run();
+}
+
+//-----------------------------------------------------------------------------
+// Version of AppMain used by console applications
+//-----------------------------------------------------------------------------
+int AppMain( int argc, char **argv, CAppSystemGroup *pAppSystemGroup )
+{
+ Assert( pAppSystemGroup );
+
+// g_pDefaultLoggingListener = &s_SimpleLoggingListener;
+ s_HInstance = NULL;
+#if !defined( _X360 )
+ CommandLine()->CreateCmdLine( argc, argv );
+#else
+ SetupEnvironment360();
+#endif
+
+ return pAppSystemGroup->Run();
+}
+
+//-----------------------------------------------------------------------------
+// Used to startup/shutdown the application
+//-----------------------------------------------------------------------------
+int AppStartup( void* hInstance, void* hPrevInstance, const char* lpCmdLine, int nCmdShow, CAppSystemGroup *pAppSystemGroup )
+{
+ Assert( pAppSystemGroup );
+
+// g_pDefaultLoggingListener = &s_SimpleWindowsLoggingListener;
+ s_HInstance = (HINSTANCE)hInstance;
+#if !defined( _X360 )
+ CommandLine()->CreateCmdLine( ::GetCommandLine() );
+#else
+ SetupEnvironment360();
+#endif
+
+ return pAppSystemGroup->Startup();
+}
+
+int AppStartup( int argc, char **argv, CAppSystemGroup *pAppSystemGroup )
+{
+ Assert( pAppSystemGroup );
+
+// g_pDefaultLoggingListener = &s_SimpleLoggingListener;
+ s_HInstance = NULL;
+#if !defined( _X360 )
+ CommandLine()->CreateCmdLine( argc, argv );
+#else
+ SetupEnvironment360();
+#endif
+
+ return pAppSystemGroup->Startup();
+}
+
+void AppShutdown( CAppSystemGroup *pAppSystemGroup )
+{
+ Assert( pAppSystemGroup );
+ pAppSystemGroup->Shutdown();
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Default implementation of an application meant to be run using Steam
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CSteamApplication::CSteamApplication( CSteamAppSystemGroup *pAppSystemGroup )
+{
+ m_pChildAppSystemGroup = pAppSystemGroup;
+ m_pFileSystem = NULL;
+ m_bSteam = false;
+}
+
+//-----------------------------------------------------------------------------
+// Create necessary interfaces
+//-----------------------------------------------------------------------------
+bool CSteamApplication::Create()
+{
+ FileSystem_SetErrorMode( FS_ERRORMODE_AUTO );
+
+ char pFileSystemDLL[MAX_PATH];
+ if ( FileSystem_GetFileSystemDLLName( pFileSystemDLL, MAX_PATH, m_bSteam ) != FS_OK )
+ return false;
+
+ // Add in the cvar factory
+ AppModule_t cvarModule = LoadModule( VStdLib_GetICVarFactory() );
+ AddSystem( cvarModule, CVAR_INTERFACE_VERSION );
+
+ AppModule_t fileSystemModule = LoadModule( pFileSystemDLL );
+ m_pFileSystem = (IFileSystem*)AddSystem( fileSystemModule, FILESYSTEM_INTERFACE_VERSION );
+ if ( !m_pFileSystem )
+ {
+ Error( "Unable to load %s", pFileSystemDLL );
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// The file system pointer is invalid at this point
+//-----------------------------------------------------------------------------
+void CSteamApplication::Destroy()
+{
+ m_pFileSystem = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Pre-init, shutdown
+//-----------------------------------------------------------------------------
+bool CSteamApplication::PreInit()
+{
+ return true;
+}
+
+void CSteamApplication::PostShutdown()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Run steam main loop
+//-----------------------------------------------------------------------------
+int CSteamApplication::Main()
+{
+ // Now that Steam is loaded, we can load up main libraries through steam
+ if ( FileSystem_SetBasePaths( m_pFileSystem ) != FS_OK )
+ return 0;
+
+ m_pChildAppSystemGroup->Setup( m_pFileSystem, this );
+ return m_pChildAppSystemGroup->Run();
+}
+
+//-----------------------------------------------------------------------------
+// Use this version in cases where you can't control the main loop and
+// expect to be ticked
+//-----------------------------------------------------------------------------
+int CSteamApplication::Startup()
+{
+ int nRetVal = BaseClass::Startup();
+ if ( GetErrorStage() != NONE )
+ return nRetVal;
+
+ if ( FileSystem_SetBasePaths( m_pFileSystem ) != FS_OK )
+ return 0;
+
+ // Now that Steam is loaded, we can load up main libraries through steam
+ m_pChildAppSystemGroup->Setup( m_pFileSystem, this );
+ return m_pChildAppSystemGroup->Startup();
+}
+
+void CSteamApplication::Shutdown()
+{
+ m_pChildAppSystemGroup->Shutdown();
+ BaseClass::Shutdown();
+}
+
+#endif
diff --git a/appframework/appframework.vpc b/appframework/appframework.vpc
new file mode 100644
index 0000000..0da76b4
--- /dev/null
+++ b/appframework/appframework.vpc
@@ -0,0 +1,53 @@
+//-----------------------------------------------------------------------------
+// APPFRAMEWORK.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$macro SRCDIR ".."
+
+$include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
+
+$Configuration
+{
+ $General
+ {
+ $AdditionalProjectDependencies "$BASE;togl" [!$IS_LIB_PROJECT && $GL]
+ }
+
+ $Linker [$OSXALL]
+ {
+ $SystemFrameworks "Carbon;OpenGL;Quartz;Cocoa;IOKit"
+ }
+}
+
+$Project "appframework"
+{
+ $Folder "Source Files"
+ {
+ $File "AppSystemGroup.cpp"
+ $File "$SRCDIR\public\filesystem_init.cpp"
+ $File "vguimatsysapp.cpp" [$WIN32]
+ $File "winapp.cpp" [$WIN32]
+ $File "posixapp.cpp" [$POSIX]
+ $File "sdlmgr.cpp" [$SDL]
+ $File "glmrendererinfo_osx.mm" [$OSXALL]
+ }
+
+ $Folder "Interface"
+ {
+ $File "$SRCDIR\public\appframework\AppFramework.h"
+ $File "$SRCDIR\public\appframework\IAppSystem.h"
+ $File "$SRCDIR\public\appframework\IAppSystemGroup.h"
+ $File "$SRCDIR\public\appframework\tier2app.h"
+ $File "$SRCDIR\public\appframework\tier3app.h"
+ $File "$SRCDIR\public\appframework\VguiMatSysApp.h"
+ $File "$SRCDIR\public\appframework\ilaunchermgr.h"
+ }
+
+ $Folder "Link Libraries"
+ {
+ $ImpLib togl [!$IS_LIB_PROJECT && $GL]
+ $ImpLib SDL2 [!$IS_LIB_PROJECT && $SDL]
+ }
+}
diff --git a/appframework/glmdisplaydb_linuxwin.inl b/appframework/glmdisplaydb_linuxwin.inl
new file mode 100644
index 0000000..3e1d76a
--- /dev/null
+++ b/appframework/glmdisplaydb_linuxwin.inl
@@ -0,0 +1,630 @@
+//========= 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: $
+//=============================================================================//
+
+//===============================================================================
+
+GLMRendererInfo::GLMRendererInfo( void )
+{
+ m_display = NULL;
+ Q_memset( &m_info, 0, sizeof( m_info ) );
+}
+
+GLMRendererInfo::~GLMRendererInfo( void )
+{
+ SDLAPP_FUNC;
+
+ if (m_display)
+ {
+ delete m_display;
+ m_display = NULL;
+ }
+}
+
+// !!! FIXME: sync this function with the Mac version in case anything important has changed.
+void GLMRendererInfo::Init( GLMRendererInfoFields *info )
+{
+ SDLAPP_FUNC;
+
+ m_info = *info;
+ m_display = NULL;
+
+ m_info.m_fullscreen = 0;
+ m_info.m_accelerated = 1;
+ m_info.m_windowed = 1;
+
+ m_info.m_ati = true;
+ m_info.m_atiNewer = true;
+
+ m_info.m_hasGammaWrites = true;
+
+ // If you haven't created a GL context by now (and initialized gGL), you're about to crash.
+
+ m_info.m_hasMixedAttachmentSizes = gGL->m_bHave_GL_ARB_framebuffer_object;
+ m_info.m_hasBGRA = gGL->m_bHave_GL_EXT_vertex_array_bgra;
+
+ // !!! FIXME: what do these do on the Mac?
+ m_info.m_hasNewFullscreenMode = false;
+ m_info.m_hasNativeClipVertexMode = true;
+
+ // 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 = gGL->m_bHave_GL_ARB_occlusion_query;
+ m_info.m_hasFramebufferBlit = gGL->m_bHave_GL_EXT_framebuffer_blit || gGL->m_bHave_GL_ARB_framebuffer_object;
+
+ GLint nMaxAniso = 0;
+ gGL->glGetIntegerv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &nMaxAniso );
+ m_info.m_maxAniso = clamp<int>( nMaxAniso, 0, 16 );
+
+ // We don't currently used bindable uniforms, but I've been experimenting with them so I might as well check this in just in case they turn out to be useful.
+ m_info.m_hasBindableUniforms = gGL->m_bHave_GL_EXT_bindable_uniform;
+ m_info.m_hasBindableUniforms = false; // !!! FIXME hardwiring this path to false until we see how to accelerate it properly
+ m_info.m_maxVertexBindableUniforms = 0;
+ m_info.m_maxFragmentBindableUniforms = 0;
+ m_info.m_maxBindableUniformSize = 0;
+
+ if (m_info.m_hasBindableUniforms)
+ {
+ gGL->glGetIntegerv(GL_MAX_VERTEX_BINDABLE_UNIFORMS_EXT, &m_info.m_maxVertexBindableUniforms);
+ gGL->glGetIntegerv(GL_MAX_FRAGMENT_BINDABLE_UNIFORMS_EXT, &m_info.m_maxFragmentBindableUniforms);
+ gGL->glGetIntegerv(GL_MAX_BINDABLE_UNIFORM_SIZE_EXT, &m_info.m_maxBindableUniformSize);
+ if ( ( m_info.m_maxVertexBindableUniforms < 1 ) || ( m_info.m_maxFragmentBindableUniforms < 1 ) || ( m_info.m_maxBindableUniformSize < ( sizeof( float ) * 4 * 256 ) ) )
+ {
+ m_info.m_hasBindableUniforms = false;
+ }
+ }
+
+ m_info.m_hasUniformBuffers = gGL->m_bHave_GL_ARB_uniform_buffer;
+ m_info.m_hasPerfPackage1 = true; // this flag is Mac-specific. We do slower things if you don't have Mac OS X 10.x.y or later. Linux always does the fast path!
+
+ //-------------------------------------------------------------------
+ // runtime options that aren't negotiable once set
+
+ m_info.m_hasDualShaders = CommandLine()->FindParm("-glmdualshaders") != 0;
+
+ //-------------------------------------------------------------------
+ // "can'ts "
+
+#if defined( OSX )
+ m_info.m_cantBlitReliably = m_info.m_intel; //FIXME X3100&10.6.3 has problems blitting.. adjust this if bug fixed in 10.6.4
+#else
+ // m_cantBlitReliably path doesn't work right now, and the Intel path is different for us on Linux/Win7 anyway
+ m_info.m_cantBlitReliably = false;
+#endif
+
+ 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
+ }
+
+ // MSAA resolve issues
+ m_info.m_cantResolveFlipped = false;
+
+
+#if defined( OSX )
+ m_info.m_cantResolveScaled = true; // generally true until new extension ships
+#else
+ // DON'T just slam this to false and run without first testing with -gl_debug enabled on NVidia/AMD/etc.
+ // This path needs the m_bHave_GL_EXT_framebuffer_multisample_blit_scaled extension.
+ m_info.m_cantResolveScaled = true;
+
+ if ( gGL->m_bHave_GL_EXT_framebuffer_multisample_blit_scaled )
+ {
+ m_info.m_cantResolveScaled = false;
+ }
+#endif
+
+ // gamma decode impacting shader codegen
+ m_info.m_costlyGammaFlips = false;
+}
+
+void GLMRendererInfo::PopulateDisplays()
+{
+ SDLAPP_FUNC;
+
+ Assert( !m_display );
+ m_display = new GLMDisplayInfo;
+
+ // Populate display mode table.
+ m_display->PopulateModes();
+}
+
+
+void GLMRendererInfo::Dump( int which )
+{
+ SDLAPP_FUNC;
+
+ GLMPRINTF(("\n #%d: GLMRendererInfo @ %p, renderer-id=(%08x) display-mask=%08x vram=%dMB",
+ which, this,
+ 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
+ ));
+
+ m_display->Dump( which );
+}
+
+
+
+
+GLMDisplayDB::GLMDisplayDB ()
+{
+ SDLAPP_FUNC;
+
+ m_renderer.m_display = NULL;
+}
+
+GLMDisplayDB::~GLMDisplayDB ( void )
+{
+ SDLAPP_FUNC;
+
+ if ( m_renderer.m_display )
+ {
+ delete m_renderer.m_display;
+ m_renderer.m_display = NULL;
+ }
+}
+
+#ifndef GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX
+#define GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX 0x9047
+#endif
+
+#ifndef GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX
+#define GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX 0x9048
+#endif
+
+#ifndef GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX
+#define GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX 0x9049
+#endif
+
+#ifndef GL_VBO_FREE_MEMORY_ATI
+#define GL_VBO_FREE_MEMORY_ATI 0x87FB
+#endif
+
+#ifndef GL_TEXTURE_FREE_MEMORY_ATI
+#define GL_TEXTURE_FREE_MEMORY_ATI 0x87FC
+#endif
+
+#ifndef GL_RENDERBUFFER_FREE_MEMORY_ATI
+#define GL_RENDERBUFFER_FREE_MEMORY_ATI 0x87FD
+#endif
+
+void GLMDisplayDB::PopulateRenderers( void )
+{
+ SDLAPP_FUNC;
+
+ Assert( !m_renderer.m_display );
+
+ GLMRendererInfoFields fields;
+ memset( &fields, 0, sizeof(fields) );
+
+ // Assume 512MB of available video memory
+ fields.m_vidMemory = 512 * 1024 * 1024;
+
+ DebugPrintf( "GL_NVX_gpu_memory_info: %s\n", gGL->m_bHave_GL_NVX_gpu_memory_info ? "AVAILABLE" : "UNAVAILABLE" );
+ DebugPrintf( "GL_ATI_meminfo: %s\n", gGL->m_bHave_GL_ATI_meminfo ? "AVAILABLE" : "UNAVAILABLE" );
+
+ if ( gGL->m_bHave_GL_NVX_gpu_memory_info )
+ {
+ gGL->glGetError();
+
+ GLint nTotalDedicated = 0, nTotalAvail = 0, nCurrentAvail = 0;
+ gGL->glGetIntegerv( GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &nTotalDedicated );
+ gGL->glGetIntegerv( GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, &nTotalAvail );
+ gGL->glGetIntegerv( GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &nCurrentAvail );
+
+ if ( gGL->glGetError() )
+ {
+ DebugPrintf( "GL_NVX_gpu_memory_info: Failed retrieving available GPU memory\n" );
+ }
+ else
+ {
+ DebugPrintf( "GL_NVX_gpu_memory_info: Total Dedicated: %u, Total Avail: %u, Current Avail: %u\n", nTotalDedicated, nTotalAvail, nCurrentAvail );
+
+ // Try to do something reasonable. Should we report dedicated or total available to the engine here?
+ // For now, just take the MAX of both.
+ uint64 nActualAvail = static_cast<uint64>( MAX( nTotalAvail, nTotalDedicated ) ) * 1024;
+ fields.m_vidMemory = static_cast< GLint >( MIN( nActualAvail, 0x7FFFFFFF ) );
+ }
+ }
+ else if ( gGL->m_bHave_GL_ATI_meminfo )
+ {
+ // As of 10/8/12 this extension is only available under Linux and Windows FireGL parts.
+ gGL->glGetError();
+
+ GLint nAvail[4] = { 0, 0, 0, 0 };
+ gGL->glGetIntegerv( GL_TEXTURE_FREE_MEMORY_ATI, nAvail );
+
+ if ( gGL->glGetError() )
+ {
+ DebugPrintf( "GL_ATI_meminfo: Failed retrieving available GPU memory\n" );
+ }
+ else
+ {
+ // param[0] - total memory free in the pool
+ // param[1] - largest available free block in the pool
+ // param[2] - total auxiliary memory free
+ // param[3] - largest auxiliary free block
+
+ DebugPrintf( "GL_ATI_meminfo: GL_TEXTURE_FREE_MEMORY_ATI: Total Free: %i, Largest Avail: %i, Total Aux: %i, Largest Aux Avail: %i\n",
+ nAvail[0], nAvail[1], nAvail[2], nAvail[3] );
+
+ uint64 nActualAvail = static_cast<uint64>( nAvail[0] ) * 1024;
+ fields.m_vidMemory = static_cast< GLint >( MIN( nActualAvail, 0x7FFFFFFF ) );
+ }
+ }
+
+ // Clamp the min amount of video memory to 256MB in case a query returned something bogus, or we interpreted it badly.
+ fields.m_vidMemory = MAX( fields.m_vidMemory, 128 * 1024 * 1024 );
+ fields.m_texMemory = fields.m_vidMemory;
+
+ fields.m_pciVendorID = GLM_OPENGL_VENDOR_ID;
+ fields.m_pciDeviceID = GLM_OPENGL_DEFAULT_DEVICE_ID;
+ if ( ( gGL->m_nDriverProvider == cGLDriverProviderIntel ) || ( gGL->m_nDriverProvider == cGLDriverProviderIntelOpenSource ) )
+ {
+ fields.m_pciDeviceID = GLM_OPENGL_LOW_PERF_DEVICE_ID;
+ }
+
+/* fields.m_colorModes = (uint)-1;
+ fields.m_bufferModes = (uint)-1;
+ fields.m_depthModes = (uint)-1;
+ fields.m_stencilModes = (uint)-1;
+ fields.m_maxAuxBuffers = (uint)128;
+ fields.m_maxSampleBuffers = (uint)128;
+ fields.m_maxSamples = (uint)2048;
+ fields.m_sampleModes = (uint)128;
+ fields.m_sampleAlpha = (uint)32;
+*/
+
+ GLint nMaxMultiSamples = 0;
+ gGL->glGetIntegerv( GL_MAX_SAMPLES_EXT, &nMaxMultiSamples );
+ fields.m_maxSamples = clamp<int>( nMaxMultiSamples, 0, 8 );
+ DebugPrintf( "GL_MAX_SAMPLES_EXT: %i\n", nMaxMultiSamples );
+
+ // We only have one GLMRendererInfo on Linux, unlike Mac OS X. Whatever libGL.so wants to do, we go with it.
+ m_renderer.Init( &fields );
+
+ // then go back and ask each renderer to populate its display info table.
+ m_renderer.PopulateDisplays();
+}
+
+
+
+void GLMDisplayDB::PopulateFakeAdapters( uint realRendererIndex ) // fake adapters = one real adapter times however many displays are on it
+{
+ SDLAPP_FUNC;
+
+ Assert( realRendererIndex == 0 );
+}
+
+void GLMDisplayDB::Populate(void)
+{
+ SDLAPP_FUNC;
+
+ this->PopulateRenderers();
+
+ this->PopulateFakeAdapters( 0 );
+
+ #if GLMDEBUG
+ this->Dump();
+ #endif
+}
+
+
+
+int GLMDisplayDB::GetFakeAdapterCount( void )
+{
+ SDLAPP_FUNC;
+
+ return 1;
+}
+
+bool GLMDisplayDB::GetFakeAdapterInfo( int fakeAdapterIndex, int *rendererOut, int *displayOut, GLMRendererInfoFields *rendererInfoOut, GLMDisplayInfoFields *displayInfoOut )
+{
+ SDLAPP_FUNC;
+
+ if (fakeAdapterIndex >= GetFakeAdapterCount() )
+ {
+ *rendererOut = 0;
+ *displayOut = 0;
+ return true; // fail
+ }
+
+ *rendererOut = 0;
+ *displayOut = 0;
+
+ bool rendResult = GetRendererInfo( *rendererOut, rendererInfoOut );
+ bool dispResult = GetDisplayInfo( *rendererOut, *displayOut, displayInfoOut );
+
+ return rendResult || dispResult;
+}
+
+
+int GLMDisplayDB::GetRendererCount( void )
+{
+ SDLAPP_FUNC;
+
+ return 1;
+}
+
+bool GLMDisplayDB::GetRendererInfo( int rendererIndex, GLMRendererInfoFields *infoOut )
+{
+ SDLAPP_FUNC;
+
+ memset( infoOut, 0, sizeof( GLMRendererInfoFields ) );
+
+ if (rendererIndex >= GetRendererCount())
+ return true; // fail
+
+ *infoOut = m_renderer.m_info;
+
+ return false;
+}
+
+int GLMDisplayDB::GetDisplayCount( int rendererIndex )
+{
+ SDLAPP_FUNC;
+
+ if (rendererIndex >= GetRendererCount())
+ {
+ Assert( 0 );
+ return 0; // fail
+ }
+
+ return 1;
+}
+
+bool GLMDisplayDB::GetDisplayInfo( int rendererIndex, int displayIndex, GLMDisplayInfoFields *infoOut )
+{
+ SDLAPP_FUNC;
+
+ memset( infoOut, 0, sizeof( GLMDisplayInfoFields ) );
+
+ if (rendererIndex >= GetRendererCount())
+ return true; // fail
+
+ if (displayIndex >= GetDisplayCount(rendererIndex))
+ return true; // fail
+
+ *infoOut = m_renderer.m_display->m_info;
+
+ return false;
+}
+
+int GLMDisplayDB::GetModeCount( int rendererIndex, int displayIndex )
+{
+ SDLAPP_FUNC;
+
+ if (rendererIndex >= GetRendererCount())
+ return 0; // fail
+
+ if (displayIndex >= GetDisplayCount(rendererIndex))
+ return 0; // fail
+
+ return m_renderer.m_display->m_modes->Count();
+}
+
+bool GLMDisplayDB::GetModeInfo( int rendererIndex, int displayIndex, int modeIndex, GLMDisplayModeInfoFields *infoOut )
+{
+ SDLAPP_FUNC;
+
+ 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_renderer.m_display->m_modes->Element( modeIndex );
+
+ *infoOut = displayModeInfo->m_info;
+ }
+ else
+ {
+ const GLMDisplayInfoFields &info = m_renderer.m_display->m_info;
+
+ infoOut->m_modePixelWidth = info.m_displayPixelWidth;
+ infoOut->m_modePixelHeight = info.m_displayPixelHeight;
+ infoOut->m_modeRefreshHz = 0;
+
+ //return true; // fail
+ }
+
+ return false;
+}
+
+
+void GLMDisplayDB::Dump( void )
+{
+ SDLAPP_FUNC;
+
+ GLMPRINTF(("\n GLMDisplayDB @ %p ",this ));
+
+ m_renderer.Dump( 0 );
+}
+
+//===============================================================================
+
+GLMDisplayInfo::GLMDisplayInfo()
+{
+ SDLAPP_FUNC;
+
+ m_modes = NULL;
+
+ int Width, Height;
+ GetLargestDisplaySize( Width, Height );
+
+ m_info.m_displayPixelWidth = ( uint )Width;
+ m_info.m_displayPixelHeight = ( uint )Height;
+}
+
+GLMDisplayInfo::~GLMDisplayInfo( void )
+{
+ SDLAPP_FUNC;
+}
+
+extern "C" int DisplayModeSortFunction( GLMDisplayMode * const *A, GLMDisplayMode * const *B )
+{
+ SDLAPP_FUNC;
+
+ 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 )
+{
+ SDLAPP_FUNC;
+
+ Assert( !m_modes );
+ m_modes = new CUtlVector< GLMDisplayMode* >;
+
+ int nummodes = SDL_GetNumVideoDisplays();
+
+ for ( int i = 0; i < nummodes; i++ )
+ {
+ SDL_Rect rect = { 0, 0, 0, 0 };
+
+ if ( !SDL_GetDisplayBounds( i, &rect ) && rect.w && rect.h )
+ {
+ m_modes->AddToTail( new GLMDisplayMode( rect.w, rect.h, 0 ) );
+ }
+ }
+
+ // Add a big pile of window resolutions.
+ static const struct
+ {
+ uint w;
+ uint h;
+ } s_Resolutions[] =
+ {
+ { 640, 480 }, // 4x3
+ { 800, 600 },
+ { 1024, 768 },
+ { 1152, 864 },
+ { 1280, 960 },
+ { 1600, 1200 },
+ { 1920, 1440 },
+ { 2048, 1536 },
+
+ { 1280, 720 }, // 16x9
+ { 1366, 768 },
+ { 1600, 900 },
+ { 1920, 1080 },
+
+ { 720, 480 }, // 16x10
+ { 1280, 800 },
+ { 1680, 1050 },
+ { 1920, 1200 },
+ { 2560, 1600 },
+ };
+
+ for ( int i = 0; i < ARRAYSIZE( s_Resolutions ); i++ )
+ {
+ uint w = s_Resolutions[ i ].w;
+ uint h = s_Resolutions[ i ].h;
+
+ if ( ( w <= m_info.m_displayPixelWidth ) && ( h <= m_info.m_displayPixelHeight ) )
+ {
+ m_modes->AddToTail( new GLMDisplayMode( w, h, 0 ) );
+
+ if ( ( w * 2 <= m_info.m_displayPixelWidth ) && ( h * 2 < m_info.m_displayPixelHeight ) )
+ {
+ // Add double of everything also - Retina proofing hopefully.
+ m_modes->AddToTail( new GLMDisplayMode( w * 2, h * 2, 0 ) );
+ }
+ }
+ }
+
+ m_modes->Sort( DisplayModeSortFunction );
+
+ // remove dupes.
+ nummodes = m_modes->Count();
+ int i = 1; // not zero!
+ while (i < nummodes)
+ {
+ GLMDisplayModeInfoFields& info0 = m_modes->Element( i - 1 )->m_info;
+ GLMDisplayModeInfoFields& info1 = m_modes->Element( i )->m_info;
+
+ if ( ( info0.m_modePixelWidth == info1.m_modePixelWidth ) &&
+ ( info0.m_modePixelHeight == info1.m_modePixelHeight ) &&
+ ( info0.m_modeRefreshHz == info1.m_modeRefreshHz ) )
+ {
+ m_modes->Remove(i);
+ nummodes--;
+ }
+ else
+ {
+ i++;
+ }
+ }
+}
+
+
+void GLMDisplayInfo::Dump( int which )
+{
+ SDLAPP_FUNC;
+
+ GLMPRINTF(("\n #%d: GLMDisplayInfo @ %08x, pixwidth=%d pixheight=%d",
+ which, (int)this, m_info.m_displayPixelWidth, m_info.m_displayPixelHeight ));
+
+ FOR_EACH_VEC( *m_modes, i )
+ {
+ ( *m_modes )[i]->Dump(i);
+ }
+}
diff --git a/appframework/glmrendererinfo_osx.mm b/appframework/glmrendererinfo_osx.mm
new file mode 100644
index 0000000..fbaf315
--- /dev/null
+++ b/appframework/glmrendererinfo_osx.mm
@@ -0,0 +1,1583 @@
+//========= 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);
+ }
+}
diff --git a/appframework/posixapp.cpp b/appframework/posixapp.cpp
new file mode 100644
index 0000000..cae3804
--- /dev/null
+++ b/appframework/posixapp.cpp
@@ -0,0 +1,182 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Pieces of the application framework, shared between POSIX systems (Mac OS X, Linux, etc)
+//
+// $Revision: $
+// $NoKeywords: $
+//=============================================================================//
+#include "appframework/AppFramework.h"
+#include "tier0/dbg.h"
+#include "tier0/icommandline.h"
+#include "interface.h"
+#include "filesystem.h"
+#include "appframework/IAppSystemGroup.h"
+#include "filesystem_init.h"
+#include "tier1/convar.h"
+#include "vstdlib/cvar.h"
+#include "togl/rendermechanism.h"
+
+// NOTE: This has to be the last file included! (turned off below, since this is included like a header)
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------------
+// Globals...
+//-----------------------------------------------------------------------------
+HINSTANCE s_HInstance;
+
+//#if !defined(LINUX)
+//static CSimpleLoggingListener s_SimpleLoggingListener;
+//ILoggingListener *g_pDefaultLoggingListener = &s_SimpleLoggingListener;
+//#endif
+
+//-----------------------------------------------------------------------------
+// HACK: Since I don't want to refit vgui yet...
+//-----------------------------------------------------------------------------
+void *GetAppInstance()
+{
+ return s_HInstance;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the application instance, should only be used if you're not calling AppMain.
+//-----------------------------------------------------------------------------
+void SetAppInstance( void* hInstance )
+{
+ s_HInstance = (HINSTANCE)hInstance;
+}
+
+
+//-----------------------------------------------------------------------------
+// Version of AppMain used by windows applications
+//-----------------------------------------------------------------------------
+
+int AppMain( void* hInstance, void* hPrevInstance, const char* lpCmdLine, int nCmdShow, CAppSystemGroup *pAppSystemGroup )
+{
+ Assert( 0 );
+ return -1;
+}
+
+//#if !defined(LINUX)
+//static CNonFatalLoggingResponsePolicy s_NonFatalLoggingResponsePolicy;
+//#endif
+
+//-----------------------------------------------------------------------------
+// Version of AppMain used by console applications
+//-----------------------------------------------------------------------------
+int AppMain( int argc, char **argv, CAppSystemGroup *pAppSystemGroup )
+{
+ Assert( pAppSystemGroup );
+
+ //#if !defined(LINUX)
+ // LoggingSystem_SetLoggingResponsePolicy( &s_NonFatalLoggingResponsePolicy );
+ //#endif
+ s_HInstance = NULL;
+ CommandLine()->CreateCmdLine( argc, argv );
+
+ return pAppSystemGroup->Run( );
+}
+
+
+
+//-----------------------------------------------------------------------------
+//
+// Default implementation of an application meant to be run using Steam
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CSteamApplication::CSteamApplication( CSteamAppSystemGroup *pAppSystemGroup )
+{
+ m_pChildAppSystemGroup = pAppSystemGroup;
+ m_pFileSystem = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Create necessary interfaces
+//-----------------------------------------------------------------------------
+bool CSteamApplication::Create( )
+{
+ FileSystem_SetErrorMode( FS_ERRORMODE_NONE );
+
+ char pFileSystemDLL[MAX_PATH];
+ if ( FileSystem_GetFileSystemDLLName( pFileSystemDLL, MAX_PATH, m_bSteam ) != FS_OK )
+ return false;
+
+ // Add in the cvar factory
+ AppModule_t cvarModule = LoadModule( VStdLib_GetICVarFactory() );
+ AddSystem( cvarModule, CVAR_INTERFACE_VERSION );
+
+ AppModule_t fileSystemModule = LoadModule( pFileSystemDLL );
+ m_pFileSystem = (IFileSystem*)AddSystem( fileSystemModule, FILESYSTEM_INTERFACE_VERSION );
+ if ( !m_pFileSystem )
+ {
+ Error( "Unable to load %s", pFileSystemDLL );
+ return false;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// The file system pointer is invalid at this point
+//-----------------------------------------------------------------------------
+void CSteamApplication::Destroy()
+{
+ m_pFileSystem = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Pre-init, shutdown
+//-----------------------------------------------------------------------------
+bool CSteamApplication::PreInit( )
+{
+ return true;
+}
+
+void CSteamApplication::PostShutdown( )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Run steam main loop
+//-----------------------------------------------------------------------------
+int CSteamApplication::Main( )
+{
+ // Now that Steam is loaded, we can load up main libraries through steam
+ m_pChildAppSystemGroup->Setup( m_pFileSystem, this );
+ return m_pChildAppSystemGroup->Run( );
+}
+
+
+int CSteamApplication::Startup()
+{
+ int nRetVal = BaseClass::Startup();
+ if ( GetErrorStage() != NONE )
+ return nRetVal;
+
+ if ( FileSystem_SetBasePaths( m_pFileSystem ) != FS_OK )
+ return 0;
+
+ // Now that Steam is loaded, we can load up main libraries through steam
+ m_pChildAppSystemGroup->Setup( m_pFileSystem, this );
+ return m_pChildAppSystemGroup->Startup();
+}
+
+
+void CSteamApplication::Shutdown()
+{
+ m_pChildAppSystemGroup->Shutdown();
+ BaseClass::Shutdown();
+}
+
+// Turn off memdbg macros (turned on up top) since this is included like a header
+#include "tier0/memdbgoff.h"
+
diff --git a/appframework/sdlmgr.cpp b/appframework/sdlmgr.cpp
new file mode 100644
index 0000000..5d12180
--- /dev/null
+++ b/appframework/sdlmgr.cpp
@@ -0,0 +1,2078 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: An application framework
+//
+//=============================================================================//
+
+#include "SDL.h"
+#include "SDL_opengl.h"
+
+#include "appframework/ilaunchermgr.h"
+#include "inputsystem/ButtonCode.h"
+
+#include "togl/rendermechanism.h"
+
+#include "tier0/vprof_telemetry.h"
+#include "tier0/icommandline.h"
+
+#include "tier1/utllinkedlist.h"
+#include "tier1/convar.h"
+
+// NOTE: This has to be the last file included! (turned off below, since this is included like a header)
+#include "tier0/memdbgon.h"
+
+#ifdef GLMPRINTF
+#undef GLMPRINTF
+#endif
+
+#if GLMDEBUG
+#define GLMPRINTF(args) printf args
+#else
+#define GLMPRINTF(args)
+#endif
+
+#ifdef OSX
+ConVar osx_rawinput_set_one_time( "osx_rawinput_set_one_time", "0", FCVAR_ARCHIVE|FCVAR_HIDDEN, "");
+#endif
+
+ConVar gl_blit_halfx( "gl_blit_halfx", "0" );
+ConVar gl_blit_halfy( "gl_blit_halfy", "0" );
+ConVar gl_swapdebug( "gl_swapdebug", "0");
+ConVar gl_swaplimit( "gl_swaplimit", "0");
+ConVar gl_swapinterval( "gl_swapinterval", "0");
+ConVar gl_swaplimit_mt( "gl_swaplimit_mt", "3");
+ConVar gl_disable_forced_vsync( "gl_disable_forced_vsync", "0" );
+ConVar gl_swaptear( "gl_swaptear", "1" );
+ConVar gl_finish( "gl_finish", "0" );
+
+ConVar sdl_double_click_size( "sdl_double_click_size", "2" );
+ConVar sdl_double_click_time( "sdl_double_click_time", "400" );
+
+#if defined( DX_TO_GL_ABSTRACTION )
+COpenGLEntryPoints *gGL = NULL;
+#endif
+
+const int kBogusSwapInterval = INT_MAX;
+
+/*
+From Ryan Gordon:
+
+SDL's FULLSCREEN_DESKTOP mode on the mac now
+puts the game in its own fullscreen Space on OS X 10.7 and later, as of
+SDL 2.0.3, I think.
+
+There were several benefits to this, but it's possible (likely even)
+that Apple unhelpfully clamps you to vsync in this scenario, which would
+explain the 60fps max.
+
+There are a few options:
+- SDL_WINDOW_FULLSCREEN mode will not use this new magic (only
+SDL_WINDOW_FULLSCREEN_DESKTOP), but that brings other problems and I
+wouldn't recommend a drastic change like that.
+
+- You can force the old behavior with this hint:
+
+ SDL_SetHint(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, "1");
+
+...which must be called before SDL_Init(SDL_INIT_VIDEO) at the moment
+(that can be changed if you'd like to add a menu option that wants to
+toggle this setting at runtime, though). One can also force this with an
+environment variable, for what that's worth to most Mac users:
+
+ export SDL_VIDEO_MAC_FULLSCREEN_SPACES=0
+
+- I haven't tried it, but maybe one can force vsync off with
+SDL_GL_SetSwapInterval(), and it's just that the default is different
+for Fullscreen Spaces? Simplest solution if it works, but I don't know.
+
+You can certainly just force it off to put the game back to the way it
+worked before, but the user experience is much nicer when you can just
+slide between the game and your desktop, etc. Discounting the clamp to
+Vsync, we found that a Fullscreen Space got a slightly faster framerate,
+too (plus it's how Apple "wants" you to do fullscreen at this point, etc).
+
+And of course, this is Mac-specific: this is in the Cocoa backend, and
+thus doesn't affect Windows or Linux, etc.
+*/
+
+static void DebugPrintf( const char *pMsg, ... )
+{
+ va_list args;
+ va_start( args, pMsg );
+ char buf[2048];
+ V_vsnprintf( buf, sizeof( buf ), pMsg, args );
+ va_end( args );
+
+ Plat_DebugString( buf );
+}
+
+// #define SDLAPP_DEBUG
+#ifdef SDLAPP_DEBUG
+class LinuxAppFuncLogger
+{
+ public:
+ LinuxAppFuncLogger( const char *funcName ) : m_funcName( funcName )
+ {
+ printf( ">%s\n", m_funcName );
+ };
+
+ LinuxAppFuncLogger( const char *funcName, char *fmt, ... )
+ {
+ m_funcName = funcName;
+
+ va_list vargs;
+ va_start(vargs, fmt);
+ vprintf( fmt, vargs );
+ va_end( vargs );
+ }
+
+ ~LinuxAppFuncLogger( )
+ {
+ printf( "<%s\n", m_funcName );
+ };
+
+ const char *m_funcName;
+};
+#define SDLAPP_FUNC LinuxAppFuncLogger _logger_( __FUNCTION__ )
+#else
+#define SDLAPP_FUNC
+#endif
+
+
+#if defined( DX_TO_GL_ABSTRACTION )
+void CheckGLError( int line )
+{
+ SDLAPP_FUNC;
+
+ // Don't check this in enabled! glGetError() is extremely slow with threaded drivers.
+ return;
+ //char errbuf[1024];
+
+ //borrowed from GLMCheckError.. slightly different
+
+
+ GLenum errorcode = (GLenum)gGL->glGetError();
+ //GLenum errorcode2 = 0;
+ if ( errorcode != GL_NO_ERROR )
+ {
+ const char *decodedStr = GLMDecode( eGL_ERROR, errorcode );
+
+ printf( "\n(%d) GL Error %08x = '%s'", line, errorcode, decodedStr );
+ }
+}
+#endif
+
+//-----------------------------------------------------------------------------
+#if !defined( DEDICATED )
+
+void *VoidFnPtrLookup_GlMgr(const char *fn, bool &okay, const bool bRequired, void *fallback)
+{
+ void *retval = NULL;
+ if ((!okay) && (!bRequired)) // always look up if required (so we get a complete list of crucial missing symbols).
+ return NULL;
+
+ // The SDL path would work on all these platforms, if we were using SDL there, too...
+#if defined( USE_SDL )
+ // SDL does the right thing, so we never need to use tier0 in this case.
+ retval = SDL_GL_GetProcAddress(fn);
+ //printf("CDynamicFunctionOpenGL: SDL_GL_GetProcAddress(\"%s\") returned %p\n", fn, retval);
+ if ((retval == NULL) && (fallback != NULL))
+ {
+ //printf("CDynamicFunctionOpenGL: Using fallback %p for \"%s\"\n", fallback, fn);
+ retval = fallback;
+ }
+#else
+ #error Unimplemented
+#endif
+
+ // Note that a non-NULL response doesn't mean it's safe to call the function!
+ // You always have to check that the extension is supported;
+ // an implementation MAY return NULL in this case, but it doesn't have to (and doesn't, with the DRI drivers).
+ okay = (okay && (retval != NULL));
+ if (bRequired && !okay)
+ {
+ // We can't continue execution, because one or more GL function pointers will be NULL.
+ Error( "Could not find required OpenGL entry point '%s'! Either your video card is unsupported, or your OpenGL driver needs to be updated.\n", fn);
+ }
+
+ return retval;
+}
+
+class CSDLMgr : public ILauncherMgr
+{
+public:
+
+ CSDLMgr();
+
+// ILauncherMgr impls.
+public:
+ virtual bool Connect( CreateInterfaceFn factory );
+ virtual void Disconnect();
+
+ virtual void *QueryInterface( const char *pInterfaceName );
+
+ // Init, shutdown
+ virtual InitReturnVal_t Init();
+ virtual void Shutdown();
+
+ virtual bool CreateGameWindow( const char *pTitle, bool bWindowed, int width, int height );
+
+ virtual void IncWindowRefCount();
+ virtual void DecWindowRefCount();
+
+ // Get the next N events. The function returns the number of events that were filled into your array.
+ virtual int GetEvents( CCocoaEvent *pEvents, int nMaxEventsToReturn, bool debugEvents = false );
+#ifdef LINUX
+ virtual int PeekAndRemoveKeyboardEvents( bool *pbEsc, bool *pbReturn, bool *pbSpace, bool debugEvent = false );
+#endif
+
+ // Set the mouse cursor position.
+ virtual void SetCursorPosition( int x, int y );
+
+ virtual void *GetWindowRef() { return (void *)m_Window; }
+
+ virtual void SetWindowFullScreen( bool bFullScreen, int nWidth, int nHeight );
+ virtual bool IsWindowFullScreen() { return m_bFullScreen; }
+ virtual void MoveWindow( int x, int y );
+ virtual void SizeWindow( int width, int tall );
+ virtual void PumpWindowsMessageLoop();
+
+ virtual void DestroyGameWindow();
+ virtual void SetApplicationIcon( const char *pchAppIconFile );
+
+ virtual void GetMouseDelta( int &x, int &y, bool bIgnoreNextMouseDelta = false );
+
+ virtual void GetNativeDisplayInfo( int nDisplay, uint &nWidth, uint &nHeight, uint &nRefreshHz ); // Retrieve the size of the monitor (desktop)
+ virtual void RenderedSize( uint &width, uint &height, bool set ); // either set or retrieve rendered size value (from dxabstract)
+ virtual void DisplayedSize( uint &width, uint &height ); // query backbuffer size (window size whether FS or windowed)
+
+#if defined( DX_TO_GL_ABSTRACTION )
+ virtual void GetDesiredPixelFormatAttribsAndRendererInfo( uint **ptrOut, uint *countOut, GLMRendererInfoFields *rendInfoOut );
+
+ virtual PseudoGLContextPtr GetMainContext();
+ // Get the NSGLContext for a window's main view - note this is the carbon windowref as an argument
+ virtual PseudoGLContextPtr GetGLContextForWindow( void* windowref ) { return (PseudoGLContextPtr)m_GLContext; }
+ virtual PseudoGLContextPtr CreateExtraContext();
+ virtual void DeleteContext( PseudoGLContextPtr hContext );
+ virtual bool MakeContextCurrent( PseudoGLContextPtr hContext );
+ virtual GLMDisplayDB *GetDisplayDB( void );
+
+ virtual void ShowPixels( CShowPixelsParams *params );
+#endif
+
+ virtual void GetStackCrawl( CStackCrawlParams *params );
+
+ virtual void WaitUntilUserInput( int msSleepTime );
+
+ // Post an event to the input event queue.
+ // if debugEvent is true, post it to the debug event queue.
+ void PostEvent( const CCocoaEvent &theEvent, bool debugEvent=false );
+
+ // ask if an event is debug flavor or not.
+ bool IsDebugEvent( CCocoaEvent& event );
+
+ virtual void SetMouseVisible( bool bState );
+ virtual void SetMouseCursor( SDL_Cursor *hCursor );
+ virtual void SetForbidMouseGrab( bool bForbidMouseGrab ) { m_bForbidMouseGrab = bForbidMouseGrab; }
+
+ virtual void OnFrameRendered();
+
+ virtual void SetGammaRamp( const uint16 *pRed, const uint16 *pGreen, const uint16 *pBlue );
+
+ virtual double GetPrevGLSwapWindowTime() { return m_flPrevGLSwapWindowTime; }
+
+ // Called to create a game window that will be hidden, designed for
+ // getting an OpenGL context going so we can begin initializing things.
+ bool CreateHiddenGameWindow( const char *pTitle, int width, int height );
+
+private:
+ void handleKeyInput( const SDL_Event &event );
+
+#if defined( DX_TO_GL_ABSTRACTION )
+ SDL_GLContext m_GLContext;
+ GLuint m_readFBO;
+ GLMDisplayDB *m_displayDB;
+#endif
+
+#if defined( OSX )
+ // bool m_leopard; // true if <10.6.3 and we have to do extra work for fullscreen handling
+ bool m_force_vsync; // true if 10.6.4 + bad NV driver
+#endif
+
+ uint m_nWindowRefCount;
+
+ SDL_Window *m_Window;
+
+ bool m_bCursorVisible;
+ bool m_bSetMouseVisibleCalled;
+ SDL_Cursor *m_hCursor;
+ bool m_bSetMouseCursorCalled;
+
+ bool m_bHasFocus;
+ bool m_bFullScreen;
+ bool m_SizeWindowFullScreenState; // fullscreen state when SizeWindow() was called.
+ bool m_bForbidMouseGrab;
+
+ bool m_WindowShownAndRaised;
+
+ int m_nMouseXDelta;
+ int m_nMouseYDelta;
+
+ int m_ScreenWidth;
+ int m_ScreenHeight;
+
+ int m_renderedWidth;
+ int m_rendererHeight;
+
+ int m_WindowWidth;
+ int m_WindowHeight;
+
+ bool m_bExpectSyntheticMouseMotion;
+ int m_nMouseTargetX;
+ int m_nMouseTargetY;
+ int m_nWarpDelta;
+ bool m_bRawInput;
+
+ int m_lastKnownSwapInterval; // -1 if unknown, 0/1 otherwise
+ int m_lastKnownSwapLimit; // -1 if unknown, 0/1 otherwise
+
+ int m_pixelFormatAttribs[32];
+ int m_pixelFormatAttribCount;
+
+ float m_flMouseXScale;
+ float m_flMouseYScale;
+
+ // !!! FIXME: can we rename these from "Cocoa"?
+ CThreadMutex m_CocoaEventsMutex; // use for either queue below
+ CUtlLinkedList<CCocoaEvent,int> m_CocoaEvents;
+ CUtlLinkedList<CCocoaEvent,int> m_DebugEvents; // intercepted keys which wil be passed over to GLM
+
+ uint m_keyModifierMask;
+ uint32_t m_keyModifiers;
+ uint32_t m_mouseButtons;
+
+ bool m_bGotMouseButtonDown;
+ Uint32 m_MouseButtonDownTimeStamp;
+ int m_MouseButtonDownX;
+ int m_MouseButtonDownY;
+
+ double m_flPrevGLSwapWindowTime;
+};
+
+ILauncherMgr *g_pLauncherMgr = NULL;
+
+void* CreateSDLMgr()
+{
+ if ( g_pLauncherMgr == NULL )
+ {
+ g_pLauncherMgr = new CSDLMgr();
+ }
+ return (void *)g_pLauncherMgr;
+}
+
+// Display index where we are currently fullscreen on (or -1).
+ConVar sdl_displayindex_fullscreen( "sdl_displayindex_fullscreen", "-1", FCVAR_HIDDEN );
+
+// Display index to show window on.
+static bool g_bSDLDisplayindexSet = false;
+static void sdl_displayindex_changed( IConVar *pConVar, const char *pOldString, float flOldValue );
+ConVar sdl_displayindex( "sdl_displayindex", "0", FCVAR_HIDDEN, "SDL fullscreen display index.", sdl_displayindex_changed );
+static void sdl_displayindex_changed( IConVar *pConVar, const char *pOldString, float flOldValue )
+{
+ int NumVideoDisplays = SDL_GetNumVideoDisplays();
+
+ if ( ( sdl_displayindex.GetInt() < 0 ) || ( sdl_displayindex.GetInt() >= NumVideoDisplays ) )
+ {
+ sdl_displayindex.SetValue( 0 );
+ }
+
+ g_bSDLDisplayindexSet = true;
+}
+
+
+// Return display index of largest SDL display ( plus width & height ).
+static int GetLargestDisplaySize( int& Width, int& Height )
+{
+ int nDisplay = 0;
+
+ Width = 640;
+ Height = 480;
+
+ for ( int i = 0; i < SDL_GetNumVideoDisplays(); i++ )
+ {
+ SDL_Rect rect = { 0, 0, 0, 0 };
+
+ SDL_GetDisplayBounds( i, &rect );
+
+ if ( ( rect.w > Width ) || ( ( rect.w == Width ) && ( rect.h > Height ) ) )
+ {
+ Width = rect.w;
+ Height = rect.h;
+
+ nDisplay = i;
+ }
+ }
+
+ return nDisplay;
+}
+
+CON_COMMAND( grab_window, "grab/ungrab window." )
+{
+ if ( g_pLauncherMgr && g_pLauncherMgr->GetWindowRef() )
+ {
+ SDL_bool bGrab;
+ SDL_Window *pWindow = ( SDL_Window * )g_pLauncherMgr->GetWindowRef();
+
+ if ( args.ArgC() >= 2 )
+ {
+ bGrab = ( args[ 1 ][ 0 ] == '1' ) ? SDL_TRUE : SDL_FALSE;
+ }
+ else
+ {
+ bGrab = SDL_GetWindowGrab( pWindow ) ? SDL_FALSE : SDL_TRUE;
+ }
+
+ g_pLauncherMgr->SetForbidMouseGrab( !bGrab );
+
+ if ( bGrab != SDL_GetWindowGrab( pWindow ) )
+ {
+ Msg( "SetWindowGrab( %s )\n", bGrab ? "true" : "false" );
+ SDL_SetWindowGrab( pWindow, bGrab );
+
+ // force non-fullscreen windows to the foreground if grabbed, so you can't
+ // get your mouse locked to something in the background.
+ if ( bGrab && !g_pLauncherMgr->IsWindowFullScreen() )
+ {
+ SDL_RaiseWindow( pWindow );
+ }
+ }
+ }
+}
+
+CSDLMgr::CSDLMgr()
+{
+ m_Window = NULL;
+ Init();
+}
+
+InitReturnVal_t CSDLMgr::Init()
+{
+ SDLAPP_FUNC;
+
+ if (m_Window != NULL)
+ return INIT_OK; // already initialized.
+
+ if (!SDL_WasInit(SDL_INIT_VIDEO))
+ {
+ if (SDL_Init(SDL_INIT_VIDEO) == -1)
+ Error( "SDL_Init(SDL_INIT_VIDEO) failed: %s", SDL_GetError() );
+
+#if defined( DX_TO_GL_ABSTRACTION )
+ if ( CommandLine()->FindParm( "-gl_debug" ) )
+ {
+ SDL_GL_SetAttribute( SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG );
+ }
+
+ if (SDL_GL_LoadLibrary(NULL) == -1)
+ Error( "SDL_GL_LoadLibrary(NULL) failed: %s", SDL_GetError() );
+#endif
+ }
+
+ fprintf(stderr, "SDL video target is '%s'\n", SDL_GetCurrentVideoDriver());
+ Msg("SDL video target is '%s'\n", SDL_GetCurrentVideoDriver());
+
+ m_bForbidMouseGrab = true;
+ if ( !CommandLine()->FindParm("-nomousegrab") && CommandLine()->FindParm("-mousegrab") )
+ {
+ m_bForbidMouseGrab = false;
+ }
+
+ m_WindowShownAndRaised = false;
+
+ m_bCursorVisible = true;
+ m_bSetMouseVisibleCalled = false;
+ m_hCursor = NULL;
+ m_bSetMouseCursorCalled = false;
+
+ m_bHasFocus = true;
+ m_keyModifiers = 0;
+ m_keyModifierMask = 0;
+ m_mouseButtons = 0;
+#if defined( DX_TO_GL_ABSTRACTION )
+ m_GLContext = NULL;
+ m_readFBO = 0;
+ m_displayDB = NULL;
+#endif
+ m_nWindowRefCount = 0;
+ m_Window = NULL;
+ m_bFullScreen = false;
+ m_SizeWindowFullScreenState = false;
+ m_nMouseXDelta = 0;
+ m_nMouseYDelta = 0;
+ m_ScreenWidth = 0;
+ m_ScreenHeight = 0;
+ m_renderedWidth = 0;
+ m_rendererHeight = 0;
+ m_WindowWidth = 0;
+ m_WindowHeight = 0;
+ m_pixelFormatAttribCount = 0;
+ m_lastKnownSwapInterval = kBogusSwapInterval;
+ m_lastKnownSwapLimit = -1;
+ m_flMouseXScale = 1.0f;
+ m_flMouseYScale = 1.0f;
+
+ m_bGotMouseButtonDown = false;
+ m_MouseButtonDownTimeStamp = 0;
+ m_MouseButtonDownX = 0;
+ m_MouseButtonDownY = 0;
+
+ m_bExpectSyntheticMouseMotion = false;
+ m_nMouseTargetX = 0;
+ m_nMouseTargetY = 0;
+ m_nWarpDelta = 0;
+ m_bRawInput = false;
+
+ m_flPrevGLSwapWindowTime = 0.0f;
+
+ memset(m_pixelFormatAttribs, '\0', sizeof (m_pixelFormatAttribs));
+
+ int *attCursor = m_pixelFormatAttribs;
+
+ #define SET_GL_ATTR(key,value) \
+ *(attCursor++) = (int) (key); \
+ *(attCursor++) = (int) (value);
+
+ SET_GL_ATTR(SDL_GL_RED_SIZE, 8);
+ SET_GL_ATTR(SDL_GL_GREEN_SIZE, 8);
+ SET_GL_ATTR(SDL_GL_BLUE_SIZE, 8);
+ SET_GL_ATTR(SDL_GL_ALPHA_SIZE, 8);
+ SET_GL_ATTR(SDL_GL_DOUBLEBUFFER, 1);
+
+#ifdef OSX
+ SET_GL_ATTR(SDL_GL_DEPTH_SIZE, 0);
+#else
+ SET_GL_ATTR(SDL_GL_DEPTH_SIZE, 24);
+ SET_GL_ATTR(SDL_GL_STENCIL_SIZE, 8);
+#endif
+
+ SET_GL_ATTR(SDL_GL_ACCELERATED_VISUAL, 1);
+
+ #undef SET_GL_ATTR
+
+ m_pixelFormatAttribCount = (attCursor - &m_pixelFormatAttribs[0]) / 2;
+
+ // we need a GL context before we dig down further, since we're calling
+ // GL entry points, but the game hasn't made a window yet. So it's time
+ // to make a window! We make a 640x480 one here, and later, when asked
+ // to really actually make a window, we just resize the one we built here.
+ if ( !CreateHiddenGameWindow( "", 640, 480 ) )
+ Error( "CreateGameWindow failed" );
+
+ SDL_HideWindow( m_Window );
+
+ return INIT_OK;
+}
+
+bool CSDLMgr::Connect( CreateInterfaceFn factory )
+{
+ SDLAPP_FUNC;
+
+ return true;
+}
+
+void CSDLMgr::Disconnect()
+{
+ SDLAPP_FUNC;
+
+}
+
+void *CSDLMgr::QueryInterface( const char *pInterfaceName )
+{
+ SDLAPP_FUNC;
+ if ( !Q_stricmp( pInterfaceName, SDLMGR_INTERFACE_VERSION ) )
+ return this;
+ return NULL;
+}
+
+void CSDLMgr::Shutdown()
+{
+ SDLAPP_FUNC;
+
+ if (gGL && m_readFBO)
+ gGL->glDeleteFramebuffersEXT(1, &m_readFBO);
+ m_readFBO = 0;
+
+ if ( m_Window )
+ {
+ // Slam down the window refcount to 1 to guarantee the main GL context and window are killed.
+ m_nWindowRefCount = 1;
+ DestroyGameWindow();
+ }
+
+ SDL_GL_UnloadLibrary();
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+}
+
+bool CSDLMgr::CreateGameWindow( const char *pTitle, bool bWindowed, int width, int height )
+{
+ SDLAPP_FUNC;
+
+ // CreateGameWindow is being called. The the game initially calls this with width and height == 0.
+ // But we don't want to show the window until it gets resized to what it should be, so we keep track as to whether
+ // ShowWindow / RaiseWindow has been called yet in here, and if not we do the SDL_ShowWindow in
+ // the MoveWindow() function down below.
+ bool bShowWindow = true;
+ m_WindowShownAndRaised = false;
+
+ if ( ( width <= 0 ) || ( height <= 0 ) )
+ {
+ bShowWindow = false;
+
+ // Don't mess with current width, height - use current (or sane defaults).
+ uint defaultWidth = 0;
+ uint defaultHeight = 0;
+ uint defaultRefreshHz = 0; // Not used
+
+ this->GetNativeDisplayInfo( -1, defaultWidth, defaultHeight, defaultRefreshHz );
+
+ if ( 0 == defaultWidth ) defaultWidth = 1024;
+ if ( 0 == defaultHeight ) defaultHeight = 768;
+
+ width = m_WindowWidth ? m_WindowWidth : defaultWidth;
+ height = m_WindowHeight ? m_WindowHeight : defaultHeight;
+ }
+
+ if ( m_Window )
+ {
+ if ( pTitle )
+ {
+ SDL_SetWindowTitle( m_Window, pTitle );
+ }
+
+ if ( ( m_bFullScreen != !bWindowed ) ||
+ ( !bWindowed && ( sdl_displayindex.GetInt() != sdl_displayindex_fullscreen.GetInt() ) ) )
+ {
+ SetWindowFullScreen( !bWindowed, width, height );
+ }
+ else if ( bShowWindow )
+ {
+ SizeWindow( width, height );
+ }
+
+ if ( bShowWindow )
+ {
+ SDL_ShowWindow( m_Window );
+ SDL_RaiseWindow( m_Window );
+
+ m_WindowShownAndRaised = true;
+ }
+
+ return true;
+ }
+
+ if ( CreateHiddenGameWindow( pTitle, width, height ) )
+ {
+ SDL_ShowWindow( m_Window );
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool CSDLMgr::CreateHiddenGameWindow( const char *pTitle, int width, int height )
+{
+ Assert( !m_Window );
+ Assert( !m_bFullScreen );
+
+ m_bFullScreen = false;
+ sdl_displayindex_fullscreen.SetValue( -1 );
+
+#if defined( DX_TO_GL_ABSTRACTION )
+ // Set up GL context...
+ const int *attrib = m_pixelFormatAttribs;
+ for (int i = 0; i < m_pixelFormatAttribCount; i++, attrib += 2)
+ SDL_GL_SetAttribute((SDL_GLattr) attrib[0], attrib[1]);
+#endif
+
+ // no window yet? Create one now!
+ m_nWindowRefCount = 1;
+
+ int x = SDL_WINDOWPOS_CENTERED;
+ int y = SDL_WINDOWPOS_CENTERED;
+ int flags = SDL_WINDOW_HIDDEN;
+#if defined( DX_TO_GL_ABSTRACTION )
+ flags |= SDL_WINDOW_OPENGL;
+#endif
+ m_Window = SDL_CreateWindow( pTitle, x, y, width, height, flags );
+
+ if (m_Window == NULL)
+ Error( "Failed to create SDL window: %s", SDL_GetError() );
+ SetAssertDialogParent( m_Window );
+
+#ifdef OSX
+
+ GLMRendererInfoFields rendererInfo;
+ GetDisplayDB()->GetRendererInfo( 0, &rendererInfo );
+ //-----------------------------------------------------------------------------------------
+ //- enforce minimum system requirements for multiplayer branch (CSS / DOD / TF2) : no GMA950, X3100, or NV G7x.
+ if (!CommandLine()->FindParm("-glmnosystemcheck")) // escape hatch
+ {
+ if ( rendererInfo.m_osComboVersion < 0x0A0607 )
+ {
+ Error( "This game requires OS X version 10.6.7 or higher" );
+ exit(1);
+ }
+ // forbidden chipsets
+ if ( rendererInfo.m_atiR5xx || rendererInfo.m_intel95x || rendererInfo.m_intel3100 || rendererInfo.m_nvG7x )
+ {
+ Error( "This game does not support this type of graphics processor" );
+ exit(1);
+ }
+ }
+#endif
+
+#if defined( DX_TO_GL_ABSTRACTION )
+ m_GLContext = SDL_GL_CreateContext(m_Window);
+ if (m_GLContext == NULL)
+ Error( "Failed to create GL context: %s", SDL_GetError() );
+
+ SDL_GL_MakeCurrent(m_Window, m_GLContext);
+
+ // !!! FIXME: note for later...we never delete this context anywhere, I think.
+ // !!! FIXME: when we do get around to that, don't forget to delete/NULL gGL!
+
+ static CDynamicFunctionOpenGL< true, const GLubyte *( APIENTRY *)(GLenum name), const GLubyte * > glGetString("glGetString");
+ static CDynamicFunctionOpenGL< true, GLvoid ( APIENTRY *)(GLenum pname, GLint *params), GLvoid > glGetIntegerv("glGetIntegerv");
+
+#ifdef DBGFLAG_ASSERT
+ const char *pszString = ( const char * )glGetString(GL_VENDOR);
+ pszString = ( const char * )glGetString(GL_RENDERER);
+ pszString = ( const char * )glGetString(GL_VERSION);
+ pszString = ( const char * )glGetString(GL_EXTENSIONS);
+
+ // If we specified -gl_debug, make sure the extension string is present now.
+ if ( CommandLine()->FindParm( "-gl_debug" ) )
+ {
+ Assert( V_strstr(pszString, "GL_ARB_debug_output") );
+ }
+#endif // DBGFLAG_ASSERT
+
+ gGL = GetOpenGLEntryPoints(VoidFnPtrLookup_GlMgr);
+
+ // It is now safe to call any base GL entry point that's supplied by gGL.
+ // You still need to explicitly test for extension entry points, though!
+
+ if ( CommandLine()->FindParm( "-gl_dump_strings" ) )
+ {
+ DebugPrintf("GL_RENDERER: %s\n", (const char *) gGL->glGetString(GL_RENDERER));
+ DebugPrintf("GL_VENDOR: %s\n", (const char *) gGL->glGetString(GL_VENDOR));
+ DebugPrintf("GL_VERSION: %s\n", (const char *) gGL->glGetString(GL_VERSION));
+ const char *exts = (const char *) gGL->glGetString(GL_EXTENSIONS);
+ DebugPrintf("GL_EXTENSIONS:%s\n", exts ? "" : NULL);
+ if (exts)
+ {
+ for (const char *ptr = exts; *ptr; ptr++)
+ DebugPrintf("%c", *ptr == ' ' ? '\n' : *ptr);
+ DebugPrintf("\n");
+ }
+ DebugPrintf("\n");
+ }
+
+ gGL->glGenFramebuffersEXT(1, &m_readFBO);
+
+ gGL->glViewport(0, 0, width, height); /* Reset The Current Viewport And Perspective Transformation */
+ gGL->glScissor(0, 0, width, height); /* Reset The Current Viewport And Perspective Transformation */
+
+ // Blank out the initial window, so we're not looking at uninitialized
+ // video RAM trash until we start proper drawing.
+ gGL->glClearColor(0,0,0,0);
+ gGL->glClear(GL_COLOR_BUFFER_BIT);
+ SDL_GL_SwapWindow(m_Window);
+ gGL->glClear(GL_COLOR_BUFFER_BIT);
+ SDL_GL_SwapWindow(m_Window);
+ gGL->glClear(GL_COLOR_BUFFER_BIT);
+ SDL_GL_SwapWindow(m_Window);
+#endif // DX_TO_GL_ABSTRACTION
+
+ m_WindowWidth = width;
+ m_WindowHeight = height;
+
+ // Update mouse warp targets (dependent on window size).
+ m_nMouseTargetX = m_WindowWidth / 2;
+ m_nMouseTargetY = m_WindowHeight / 2;
+ m_nWarpDelta = Max( m_WindowHeight / 3, 200 );
+
+ return true;
+}
+
+#if defined( DX_TO_GL_ABSTRACTION )
+
+PseudoGLContextPtr CSDLMgr::GetMainContext()
+{
+ SDLAPP_FUNC;
+
+ return (PseudoGLContextPtr)m_GLContext;
+}
+
+PseudoGLContextPtr CSDLMgr::CreateExtraContext()
+{
+ SDLAPP_FUNC;
+
+ const int *attrib = m_pixelFormatAttribs;
+ for (int i = 0; i < m_pixelFormatAttribCount; i++, attrib += 2)
+ SDL_GL_SetAttribute((SDL_GLattr) attrib[0], attrib[1]);
+
+ return (PseudoGLContextPtr) SDL_GL_CreateContext(m_Window);
+}
+
+void CSDLMgr::DeleteContext( PseudoGLContextPtr hContext )
+{
+ SDLAPP_FUNC;
+ Assert( (SDL_GLContext)hContext != m_GLContext );
+
+ // Don't delete the main one.
+ if ( (SDL_GLContext)hContext != m_GLContext )
+ {
+ if ( m_Window )
+ {
+ SDL_GL_MakeCurrent(m_Window, hContext);
+ }
+ SDL_GL_DeleteContext((SDL_GLContext) hContext);
+ }
+}
+
+bool CSDLMgr::MakeContextCurrent( PseudoGLContextPtr hContext )
+{
+ SDLAPP_FUNC;
+
+ // We only ever have one GL context on Linux at the moment, so don't spam these calls.
+ return SDL_GL_MakeCurrent(m_Window, (SDL_GLContext)hContext ) == 0;
+}
+
+#endif // DX_TO_GL_ABSTRACTION
+
+
+int CSDLMgr::GetEvents( CCocoaEvent *pEvents, int nMaxEventsToReturn, bool debugEvent )
+{
+ SDLAPP_FUNC;
+
+ m_CocoaEventsMutex.Lock();
+
+ CUtlLinkedList<CCocoaEvent,int> &queue = debugEvent ? m_CocoaEvents : m_DebugEvents;
+
+ int nAvailable = queue.Count();
+ int nToWrite = MIN( nAvailable, nMaxEventsToReturn );
+
+ CCocoaEvent *pCurEvent = pEvents;
+ for ( int i=0; i < nToWrite; i++ )
+ {
+ int iHead = queue.Head();
+ memcpy( pCurEvent, &queue[iHead], sizeof( CCocoaEvent ) );
+ queue.Remove( iHead );
+ ++pCurEvent;
+ }
+
+ m_CocoaEventsMutex.Unlock();
+
+ return nToWrite;
+}
+
+#ifdef LINUX
+
+int CSDLMgr::PeekAndRemoveKeyboardEvents( bool *pbEsc, bool *pbReturn, bool *pbSpace, bool debugEvent )
+{
+ SDLAPP_FUNC;
+
+ m_CocoaEventsMutex.Lock();
+
+ int nRead = 0;
+ CUtlLinkedList<CCocoaEvent,int> &queue = debugEvent ? m_CocoaEvents : m_DebugEvents;
+ int nEvents = queue.Count();
+
+ for ( int iEvent=0; iEvent < nEvents; iEvent++ )
+ {
+ CCocoaEvent *pEvent = &queue[ iEvent ];
+
+ switch( pEvent->m_EventType )
+ {
+ case CocoaEvent_KeyDown:
+ {
+ switch ( pEvent->m_VirtualKeyCode )
+ {
+ case SDL_SCANCODE_ESCAPE:
+ nRead++;
+ *pbEsc = true;
+ pEvent->m_EventType = CocoaEvent_Deleted;
+ break;
+ case SDL_SCANCODE_RETURN:
+ case SDL_SCANCODE_KP_ENTER:
+ nRead++;
+ *pbReturn = true;
+ pEvent->m_EventType = CocoaEvent_Deleted;
+ break;
+ case SDL_SCANCODE_SPACE:
+ nRead++;
+ *pbSpace = true;
+ pEvent->m_EventType = CocoaEvent_Deleted;
+ break;
+ }
+ }
+ }
+ }
+
+ m_CocoaEventsMutex.Unlock();
+ return nRead;
+}
+
+#endif // LINUX
+
+bool CSDLMgr::IsDebugEvent( CCocoaEvent& event )
+{
+ SDLAPP_FUNC;
+
+ bool result = false;
+
+ #if GLMDEBUG == 2
+ // simple rule for now, if the option key is involved, it's a debug key
+ // but only if GLM debugging is level 2 (specifically enabled) so we're
+ // not stealing control for normal debug builds
+ result |= ( (event.m_EventType == CocoaEvent_KeyDown) && ((event.m_ModifierKeyMask & (1<<eControlKey))!=0) );
+ #endif
+
+ return result;
+}
+
+ // Set the mouse cursor position.
+void CSDLMgr::SetCursorPosition( int x, int y )
+{
+ SDLAPP_FUNC;
+
+ SDL_WarpMouseInWindow(m_Window, x, y);
+}
+
+void CSDLMgr::PostEvent( const CCocoaEvent &theEvent, bool debugEvent )
+{
+ SDLAPP_FUNC;
+
+ m_CocoaEventsMutex.Lock();
+
+ CUtlLinkedList<CCocoaEvent,int> &queue = debugEvent ? m_CocoaEvents : m_DebugEvents;
+ queue.AddToTail( theEvent );
+
+ m_CocoaEventsMutex.Unlock();
+}
+
+void CSDLMgr::SetMouseVisible( bool bState )
+{
+ SDLAPP_FUNC;
+
+ // If this is the first time we've been called in this frame or we're setting it to visible, then store it.
+ // This is to handle the case where the game toggles the mouse state between visible and !visible 1 billion times
+ // in a frame.
+ if ( !m_bSetMouseVisibleCalled || bState )
+ {
+ m_bCursorVisible = bState;
+ m_bSetMouseVisibleCalled = true;
+ }
+}
+
+void CSDLMgr::SetMouseCursor( SDL_Cursor *hCursor )
+{
+ SDLAPP_FUNC;
+
+ if ( m_hCursor != hCursor )
+ {
+ if ( !hCursor )
+ {
+ // SDL_SetCursor( NULL ) just forces a cursor redraw, so don't ever bother doing that.
+ SetMouseVisible( false );
+ }
+ else
+ {
+ m_hCursor = hCursor;
+ }
+ m_bSetMouseCursorCalled = true;
+ }
+}
+
+void CSDLMgr::OnFrameRendered()
+{
+ SDLAPP_FUNC;
+
+ if ( m_bCursorVisible && m_bSetMouseCursorCalled )
+ {
+ SDL_SetCursor( m_hCursor );
+
+ m_bSetMouseCursorCalled = false;
+ }
+
+ if ( m_bSetMouseVisibleCalled )
+ {
+
+
+ ConVarRef rawinput( "m_rawinput" );
+
+
+#ifdef OSX
+ // We default raw input to on on Mac and set it one time for all users since
+ // it didn't used to be the default.
+ if ( !osx_rawinput_set_one_time.GetBool() )
+ {
+ osx_rawinput_set_one_time.SetValue( 1 );
+ rawinput.SetValue( 1 );
+ }
+#endif
+
+ m_bRawInput = !m_bCursorVisible && rawinput.IsValid() && rawinput.GetBool();
+
+ SDL_bool bWindowGrab = !m_bCursorVisible ? SDL_TRUE : SDL_FALSE;
+ SDL_bool bRelativeMouseMode = bWindowGrab;
+
+ if ( !m_bRawInput )
+ {
+ if ( m_bForbidMouseGrab )
+ bWindowGrab = SDL_FALSE;
+
+ bRelativeMouseMode = SDL_FALSE;
+ }
+
+ SDL_SetWindowGrab( m_Window, bWindowGrab );
+ SDL_SetRelativeMouseMode( bRelativeMouseMode );
+
+ SDL_ShowCursor( m_bCursorVisible ? 1 : 0 );
+
+ // force non-fullscreen windows to the foreground if grabbed, so you can't get your mouse locked to something in the background.
+ if ( bWindowGrab && !m_bFullScreen )
+ {
+ SDL_RaiseWindow( m_Window );
+ }
+
+ m_bSetMouseVisibleCalled = false;
+ }
+}
+
+#if defined( DX_TO_GL_ABSTRACTION )
+void CSDLMgr::ShowPixels( CShowPixelsParams *params )
+{
+ SDLAPP_FUNC;
+
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, __FUNCTION__ );
+
+ if (params->m_onlySyncView)
+ return;
+
+ int swapInterval = 0;
+ int swapLimit = 0;
+
+ if (gl_swapdebug.GetInt())
+ {
+ // just jam through these debug convars every frame
+ // but they will be shock absorbed below
+
+ swapInterval = gl_swapinterval.GetInt();
+ swapLimit = gl_swaplimit.GetInt();
+ }
+ else
+ {
+ // jam through (sync&limit) = 1 or 0..
+ swapInterval = params->m_vsyncEnable ? 1 : 0;
+ swapLimit = 1; // params->m_vsyncEnable ? 1 : 0; // no good reason to turn off swap limit in normal user mode
+
+#ifdef OSX
+ // only do the funky forced vsync for NV on 10.6.4 and only if the bypass is not turned on
+ if (m_force_vsync && (gl_disable_forced_vsync.GetInt()==0))
+ {
+ swapInterval = 1;
+ swapLimit = 1;
+ }
+#else
+ if (gl_swaptear.GetInt() && gGL->HasSwapTearExtension())
+ {
+ // For 0, do nothing. For 1, make it -1.
+ swapInterval = -swapInterval;
+ }
+#endif
+ }
+
+ // only touch them on changes, or right after a change in windowed/FS state
+ if ( (swapInterval!=m_lastKnownSwapInterval) || (swapLimit!=m_lastKnownSwapLimit) )
+ {
+
+ if (swapInterval!=m_lastKnownSwapInterval)
+ {
+ // This code hits when we turn on vsync, if we're going to swap tear.
+ // We want to do one frame of real vsync to get the engine to sync at the top
+ // of the frame refresh.
+ if (swapInterval < 0 && (m_lastKnownSwapInterval == 0 || m_lastKnownSwapInterval == kBogusSwapInterval)) {
+ swapInterval = -swapInterval;
+ }
+ SDL_GL_SetSwapInterval(swapInterval);
+ }
+
+ m_lastKnownSwapInterval = swapInterval;
+ m_lastKnownSwapLimit = swapLimit;
+
+ printf("\n ##### swap interval = %d swap limit = %d #####\n", m_lastKnownSwapInterval, m_lastKnownSwapLimit );
+ fflush(stdout);
+
+ }
+
+#ifdef OSX
+ if (!params->m_noBlit)
+ {
+ if ( params->m_useBlit ) // FBO blit path - which is what we *should* be using. But if the params say no, then don't do it because the ext is not there.
+ {
+ // bind a quickie FBO to enclose the source texture
+ GLint myreadfb = 1000;
+
+ glBindFramebufferEXT( GL_READ_FRAMEBUFFER_EXT, myreadfb);
+ CheckGLError( __LINE__ );
+
+ glBindFramebufferEXT( GL_DRAW_FRAMEBUFFER_EXT, 0); // to the default FB/backbuffer
+ CheckGLError( __LINE__ );
+
+ // attach source tex to source FB
+ glFramebufferTexture2DEXT( GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, params->m_srcTexName, 0);
+ CheckGLError( __LINE__ );
+
+ // blit
+
+ int srcxmin = 0;
+ int srcymin = 0;
+ int srcxmax = params->m_width;
+ int srcymax = params->m_height;
+
+ // normal blit
+ int dstxmin = 0;
+ int dstymin = 0;
+ int dstxmax = 0;
+ int dstymax = 0;
+
+ SDL_GetWindowSize(m_Window, &dstxmax, &dstymax);
+
+ if (gl_blit_halfx.GetInt())
+ {
+ // blit right half
+ srcxmin += srcxmax/2;
+ dstxmin += dstxmax/2;
+ }
+
+ if (gl_blit_halfy.GetInt())
+ {
+ // blit top half
+ // er, but top on screen is bottom of GL y coord range
+ srcymax /= 2;
+ dstymin += dstymax/2;
+ }
+
+ // go NEAREST if sizes match
+ GLenum filter = ( ((srcxmax-srcxmin)==(dstxmax-dstxmin)) && ((srcymax-srcymin)==(dstymax-dstymin)) ) ? GL_NEAREST : GL_LINEAR;
+
+ glBlitFramebufferEXT(
+ /* src min and maxes xy xy */ srcxmin, srcymin, srcxmax,srcymax,
+ /* dst min and maxes xy xy */ dstxmin, dstymax, dstxmax,dstymin, // note yflip here
+ GL_COLOR_BUFFER_BIT, filter );
+ CheckGLError( __LINE__ );
+
+ // detach source tex
+ glFramebufferTexture2DEXT( GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, 0, 0);
+ CheckGLError( __LINE__ );
+
+ glBindFramebufferEXT( GL_READ_FRAMEBUFFER_EXT, 0);
+ CheckGLError( __LINE__ );
+
+ glBindFramebufferEXT( GL_DRAW_FRAMEBUFFER_EXT, 0); // to the default FB/backbuffer
+ CheckGLError( __LINE__ );
+
+ }
+ else
+ {
+ // old blit - gets very dark output with sRGB sources... not good
+ bool texing = true;
+
+ glUseProgram(NULL);
+
+ glDisable( GL_DEPTH_TEST );
+ glDepthMask( GL_FALSE );
+
+ glActiveTexture( GL_TEXTURE0 );
+
+ if (texing)
+ {
+ Assert( glIsTexture (params->m_srcTexName) );
+
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture( GL_TEXTURE_2D, params->m_srcTexName );
+ CheckGLError( __LINE__ );
+
+ GLint width;
+ glGetTexLevelParameteriv( GL_TEXTURE_2D, //target
+ 0, //level,
+ GL_TEXTURE_WIDTH, //pname
+ &width
+ );
+ CheckGLError( __LINE__ );
+ }
+ else
+ {
+ glBindTexture( GL_TEXTURE_2D, 0 );
+ CheckGLError( __LINE__ );
+
+ glDisable( GL_TEXTURE_2D );
+ glColor4f( 1.0, 0.0, 0.0, 1.0 );
+ }
+
+
+ // immediate mode is fine for a simple textured quad
+ // later if we switch the Valve side to render into an RBO, then this would turn into an FBO blit
+ // note, do not check glGetError in between glBegin/glEnd, lol
+
+ // flipped
+ float topv = 0.0;
+ float botv = 1.0;
+
+ glBegin(GL_QUADS);
+
+ if (texing)
+ glTexCoord2f( 0.0, botv );
+ glVertex3f ( -1.0, -1.0, 0.0 );
+
+ if (texing)
+ glTexCoord2f( 1.0, botv );
+ glVertex3f ( 1.0, -1.0, 0.0 );
+
+ if (texing)
+ glTexCoord2f( 1.0, topv );
+ glVertex3f ( 1.0, 1.0, 0.0 );
+
+ if (texing)
+ glTexCoord2f( 0.0, topv );
+ glVertex3f ( -1.0, 1.0, 0.0 );
+ glEnd();
+ CheckGLError( __LINE__ );
+
+ if (texing)
+ {
+ glBindTexture( GL_TEXTURE_2D, 0 );
+ CheckGLError( __LINE__ );
+
+ glDisable(GL_TEXTURE_2D);
+ }
+
+ }
+ }
+#endif
+
+ if ( gl_finish.GetInt() )
+ {
+ gGL->glFinish();
+ }
+ CheckGLError( __LINE__ );
+
+ CFastTimer tm;
+ tm.Start();
+
+ SDL_GL_SwapWindow( m_Window );
+
+ m_flPrevGLSwapWindowTime = tm.GetDurationInProgress().GetMillisecondsF();
+
+ CheckGLError( __LINE__ );
+}
+#endif // DX_TO_GL_ABSTRACTION
+
+
+void CSDLMgr::SetWindowFullScreen( bool bFullScreen, int nWidth, int nHeight )
+{
+ SDLAPP_FUNC;
+
+ SDL_DisplayMode mode;
+ int displayIndex = sdl_displayindex.GetInt();
+
+ if ( bFullScreen )
+ {
+ if ( SDL_GetDesktopDisplayMode( displayIndex, &mode ) != 0 )
+ {
+ Assert( 0 );
+ SDL_GetDesktopDisplayMode( 0, &mode );
+ }
+
+ mode.format = (Uint32)SDL_PIXELFORMAT_RGBX8888;
+
+ m_flMouseXScale = ( float )nWidth / ( float )mode.w;
+ m_flMouseYScale = ( float )nHeight / ( float )mode.h;
+ }
+ else
+ {
+ mode.format = ( Uint32 )SDL_PIXELFORMAT_RGBX8888;
+ mode.refresh_rate = 0;
+ mode.w = nWidth;
+ mode.h = nHeight;
+ mode.driverdata = 0;
+ m_flMouseXScale = 1.0f;
+ m_flMouseYScale = 1.0f;
+ }
+
+ SDL_SetWindowDisplayMode( m_Window, &mode );
+
+ if ( ( m_bFullScreen != bFullScreen ) ||
+ ( bFullScreen && ( sdl_displayindex_fullscreen.GetInt() != displayIndex ) ) )
+ {
+ if ( bFullScreen )
+ {
+ int x = 0;
+ int y = 0;
+
+ // If we have more than one display, center the window in the one we've been assigned to.
+ if ( SDL_GetNumVideoDisplays() > 1 )
+ {
+ SDL_Rect rect = { 0, 0, 0, 0 };
+
+ SDL_GetDisplayBounds( displayIndex, &rect );
+
+ x = rect.x;
+ y = rect.y;
+ }
+
+ if ( m_bFullScreen == bFullScreen )
+ {
+ // TODO: Temporary workaround. SDL doesn't support going fullscreen on one monitor to fullscreen
+ // on another. So we switch to windowed here, move our window, then go back fullscreen.
+ SDL_SetWindowFullscreen( m_Window, SDL_FALSE );
+ ThreadSleep( 15 );
+ }
+
+ // Move the window to the upper left of whatever display we're on, then size to fullscreen.
+ SDL_SetWindowPosition( m_Window, x, y );
+ SizeWindow( nWidth, nHeight );
+
+ sdl_displayindex_fullscreen.SetValue( displayIndex );
+ }
+ else
+ {
+ sdl_displayindex_fullscreen.SetValue( -1 );
+ }
+
+ SDL_SetWindowFullscreen( m_Window, bFullScreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0 );
+
+ m_bFullScreen = bFullScreen;
+ }
+}
+
+
+void CSDLMgr::MoveWindow( int x, int y )
+{
+ SDLAPP_FUNC;
+
+ SDL_SetWindowPosition(m_Window, x, y);
+}
+
+void CSDLMgr::SizeWindow( int width, int tall )
+{
+ SDLAPP_FUNC;
+
+ if ( ( m_WindowWidth == width ) &&
+ ( m_WindowHeight == tall ) &&
+ ( m_SizeWindowFullScreenState == m_bFullScreen ) &&
+ m_WindowShownAndRaised )
+ {
+ return;
+ }
+
+ // Make sure we don't skip doing a SizeWindow when fullscreen state changes.
+ m_SizeWindowFullScreenState = m_bFullScreen;
+
+ m_WindowWidth = width;
+ m_WindowHeight = tall;
+
+ // Update mouse warp targets (dependent on window size).
+ m_nMouseTargetX = m_WindowWidth / 2;
+ m_nMouseTargetY = m_WindowHeight / 2;
+ m_nWarpDelta = Max( m_WindowHeight / 3, 200 );
+
+ SDL_SetWindowSize( m_Window, width, tall );
+
+#if defined( DX_TO_GL_ABSTRACTION )
+ gGL->glViewport(0, 0, (GLsizei) width, (GLsizei) tall);
+ gGL->glScissor( 0,0, (GLsizei) width, (GLsizei) tall );
+#endif
+
+ // If the Window hasn't been shown yet, show it now.
+ if ( !m_WindowShownAndRaised )
+ {
+ SDL_ShowWindow( m_Window );
+ SDL_RaiseWindow( m_Window );
+
+ m_WindowShownAndRaised = true;
+ }
+ else
+ {
+ SDL_RaiseWindow( m_Window );
+ }
+}
+
+
+// key input handler
+void CSDLMgr::handleKeyInput( const SDL_Event &event )
+{
+ SDLAPP_FUNC;
+
+ Assert( ( event.type == SDL_KEYDOWN ) || ( event.type == SDL_KEYUP ) );
+
+#ifdef OSX
+ if ( event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_TAB &&
+ SDL_GetModState()&KMOD_GUI && CommandLine()->FindParm( "-exclusivefs" ) )
+ {
+ // If we're in exclusive fullscreen mode, and they command-tab, handle
+ // that by forcing minimization of the window.
+ SDL_MinimizeWindow( m_Window );
+ }
+#endif
+
+ const bool bPressed = ( event.type == SDL_KEYDOWN );
+
+ // !!! FIXME: we should be getting text input from a different event...
+ CCocoaEvent theEvent;
+ theEvent.m_EventType = ( bPressed ) ? CocoaEvent_KeyDown : CocoaEvent_KeyUp;
+ theEvent.m_VirtualKeyCode = event.key.keysym.scancode;
+ theEvent.m_UnicodeKey = 0;
+ theEvent.m_UnicodeKeyUnmodified = 0;
+
+ // Testing for non-qwerty keyboards: work out the key name and use this to
+ // calculate the scancode.
+ if ( CommandLine()->FindParm( "-nonqwerty" ) )
+ {
+ const char* keyname = SDL_GetKeyName( event.key.keysym.sym );
+ if ( keyname != NULL && strlen( keyname ) == 1) {
+ const char c = *keyname;
+ if ( c >= 'A' && c <= 'Z' )
+ {
+ theEvent.m_VirtualKeyCode = SDL_SCANCODE_A + ( c - 'A' );
+ }
+ else
+ {
+ switch( c )
+ {
+ case '=': theEvent.m_VirtualKeyCode = SDL_SCANCODE_EQUALS; break;
+ case '-': theEvent.m_VirtualKeyCode = SDL_SCANCODE_MINUS; break;
+ case '[': theEvent.m_VirtualKeyCode = SDL_SCANCODE_LEFTBRACKET; break;
+ case ']': theEvent.m_VirtualKeyCode = SDL_SCANCODE_RIGHTBRACKET; break;
+ case ';': theEvent.m_VirtualKeyCode = SDL_SCANCODE_SEMICOLON; break;
+ case '\'': theEvent.m_VirtualKeyCode = SDL_SCANCODE_APOSTROPHE; break;
+ case ',': theEvent.m_VirtualKeyCode = SDL_SCANCODE_COMMA; break;
+ case '.': theEvent.m_VirtualKeyCode = SDL_SCANCODE_PERIOD; break;
+ case '/': theEvent.m_VirtualKeyCode = SDL_SCANCODE_SLASH; break;
+ }
+ }
+ }
+ }
+
+ // key modifiers aren't necessarily reliable in all the cases we'd want, so track it ourselves.
+ const uint32_t ModCAPSLOCK = (1 << 0);
+ const uint32_t ModSHIFTR = (1 << 1);
+ const uint32_t ModSHIFTL = (1 << 2);
+ const uint32_t ModCTRLR = (1 << 3);
+ const uint32_t ModCTRLL = (1 << 4);
+ const uint32_t ModALTR = (1 << 5);
+ const uint32_t ModALTL = (1 << 6);
+ const uint32_t ModGUIR = (1 << 7);
+ const uint32_t ModGUIL = (1 << 8);
+
+ #define KEYSYMCASE(mod,side,op,key) \
+ case SDLK_##side##mod: \
+ m_keyModifiers op Mod##mod##side; \
+ theEvent.m_VirtualKeyCode = -key; \
+ break
+
+ //bool bDropKey = false;
+ if (bPressed)
+ {
+ switch (event.key.keysym.sym)
+ {
+ KEYSYMCASE(CAPSLOCK,,|=,KEY_CAPSLOCK);
+ KEYSYMCASE(SHIFT,R,|=,KEY_RSHIFT);
+ KEYSYMCASE(SHIFT,L,|=,KEY_LSHIFT);
+ KEYSYMCASE(CTRL,R,|=,KEY_RCONTROL);
+ KEYSYMCASE(CTRL,L,|=,KEY_LCONTROL);
+ KEYSYMCASE(GUI,R,|=,KEY_RWIN);
+ KEYSYMCASE(GUI,L,|=,KEY_LWIN);
+ KEYSYMCASE(ALT,R,|=,KEY_RALT);
+ KEYSYMCASE(ALT,L,|=,KEY_LALT);
+ default: break; // don't care.
+ }
+ }
+ else
+ {
+ switch (event.key.keysym.sym)
+ {
+ KEYSYMCASE(CAPSLOCK,,&= ~,KEY_CAPSLOCK);
+ KEYSYMCASE(SHIFT,R,&= ~,KEY_RSHIFT);
+ KEYSYMCASE(SHIFT,L,&= ~,KEY_LSHIFT);
+ KEYSYMCASE(CTRL,R,&= ~,KEY_RCONTROL);
+ KEYSYMCASE(CTRL,L,&= ~,KEY_LCONTROL);
+ KEYSYMCASE(GUI,R,&= ~,KEY_RWIN);
+ KEYSYMCASE(GUI,L,&= ~,KEY_LWIN);
+ KEYSYMCASE(ALT,R,&= ~,KEY_RALT);
+ KEYSYMCASE(ALT,L,&= ~,KEY_LALT);
+ default: break; // don't care.
+ }
+ }
+
+ #undef KEYSYMCASE
+
+ m_keyModifierMask = 0;
+ if (m_keyModifiers & ModCAPSLOCK)
+ m_keyModifierMask |= (1<<eCapsLockKey);
+ if (m_keyModifiers & (ModSHIFTR | ModSHIFTL))
+ m_keyModifierMask |= (1<<eShiftKey);
+ if (m_keyModifiers & (ModCTRLR | ModCTRLL))
+ m_keyModifierMask |= (1<<eControlKey);
+ if (m_keyModifiers & (ModALTR | ModALTL))
+ m_keyModifierMask |= (1<<eAltKey);
+ if (m_keyModifiers & (ModGUIR | ModGUIL))
+ m_keyModifierMask |= (1<<eCommandKey);
+
+ theEvent.m_ModifierKeyMask = m_keyModifierMask;
+
+ // make a decision about this event - does it go in the normal evt queue or into the debug queue.
+ bool debug = IsDebugEvent( theEvent );
+
+#if GLMDEBUG
+ bool bIsShifted = ( ((theEvent.m_ModifierKeyMask & (1<<eCapsLockKey))!=0) || ((theEvent.m_ModifierKeyMask & (1<<eShiftKey))!=0) );
+ theEvent.m_UnicodeKeyUnmodified = event.key.keysym.sym;
+ if ( bIsShifted )
+ {
+ switch ( event.key.keysym.sym )
+ {
+ case '[':
+ theEvent.m_UnicodeKeyUnmodified = '{';
+ break;
+ case ']':
+ theEvent.m_UnicodeKeyUnmodified = '}';
+ break;
+ case 'h':
+ theEvent.m_UnicodeKeyUnmodified = 'H';
+ break;
+ case ',':
+ theEvent.m_UnicodeKeyUnmodified = '<';
+ break;
+ case '.':
+ theEvent.m_UnicodeKeyUnmodified = '>';
+ break;
+ }
+ }
+#endif
+
+ PostEvent( theEvent, debug );
+}
+
+void CSDLMgr::PumpWindowsMessageLoop()
+{
+ SDLAPP_FUNC;
+
+ SDL_Event event;
+ int nEventsProcessed = 0;
+ while ( SDL_PollEvent(&event) && nEventsProcessed < 100 )
+ {
+ nEventsProcessed++;
+
+ switch ( event.type )
+ {
+ case SDL_MOUSEMOTION:
+ {
+ if ( !m_bHasFocus )
+ break;
+
+ // We still handle WM_MOUSEMOVE in CInputSystem for regular mouse events, only raw goes through SDL.
+ // This is done in order to maintain legacy mouse behaviour for Windows users.
+ if ( IsWindows() && !m_bRawInput )
+ break;
+
+ // When SDL_WarpMouseInWindow is called, an SDL_MOUSEMOTION
+ // event is sent. We want to ignore such 'synthetic'
+ // mouse motion events.
+ if ( m_bExpectSyntheticMouseMotion &&
+ event.motion.x == m_nMouseTargetX &&
+ event.motion.y == m_nMouseTargetY )
+ {
+ m_bExpectSyntheticMouseMotion = false;
+ break;
+ }
+
+ m_nMouseXDelta += event.motion.xrel;
+ m_nMouseYDelta += event.motion.yrel;
+
+ if ( !m_bRawInput && !m_bCursorVisible &&
+ (event.motion.x < m_nMouseTargetX - m_nWarpDelta ||
+ event.motion.x > m_nMouseTargetX + m_nWarpDelta ||
+ event.motion.y < m_nMouseTargetY - m_nWarpDelta ||
+ event.motion.y > m_nMouseTargetY + m_nWarpDelta) )
+ {
+ // We have strayed outside of our desired area, so
+ // warp the cursor back to the middle of the window.
+ SDL_WarpMouseInWindow( m_Window, m_nMouseTargetX, m_nMouseTargetY );
+ m_bExpectSyntheticMouseMotion = true;
+ }
+
+ CCocoaEvent theEvent;
+ theEvent.m_EventType = CocoaEvent_MouseMove;
+ theEvent.m_MousePos[0] = event.motion.x * m_flMouseXScale;
+ theEvent.m_MousePos[1] = event.motion.y * m_flMouseYScale;
+ theEvent.m_MouseButtonFlags = m_mouseButtons;
+ PostEvent( theEvent );
+ break;
+ }
+
+ case SDL_MOUSEBUTTONUP:
+ case SDL_MOUSEBUTTONDOWN:
+ {
+ // SDL buttons:
+ // 1 = Left button
+ // 2 = Middle button
+ // 3 = Right button
+ // 4 = Wheel Forward ; These events are handled by SDL_MOUSEWHEEL and don't come through here.
+ // 5 = Wheel Back ;
+ // 6 = Wheel Tilt Left ;
+ // 7 = Wheel Tilt Right ;
+ // 8 = Browser Back
+ // 9 = Browser Forward
+ // 10 = Task button (probably similar to Alt-Tab)
+
+ // every other platform does left(1), right(2), middle(3)...
+ int button;
+
+ switch( event.button.button )
+ {
+ case 1: button = 1; break;
+ case 2: button = 3; break;
+ case 3: button = 2; break;
+ default:
+ // For all buttons above 4, map them to 4 & 5 forever. This is because different mice
+ // will use different mappings. Ie, mousewheel mice can do this:
+ // 4 = Wheel Forward ; These events are handled by SDL_MOUSEWHEEL and don't come through here.
+ // 5 = Wheel Back ;
+ // 6 = Wheel Tilt Left ;
+ // 7 = Wheel Tilt Right ;
+ // 8 = Browser Back
+ // 9 = Browser Forward
+ // 10 = Task button (probably similar to Alt-Tab)
+ // Mice without wheels can do 4/5 as regular 4/5, etc.
+ button = 4 + ( event.button.button & 0x1 );
+ break;
+ }
+
+ const bool bPressed = (event.type == SDL_MOUSEBUTTONDOWN);
+ const CocoaMouseButton_t cocoaButton = ( CocoaMouseButton_t )( 1 << (button - 1 ) );
+
+ if (bPressed)
+ m_mouseButtons |= cocoaButton;
+ else
+ m_mouseButtons &= ~cocoaButton;
+
+ bool bDoublePress = false;
+
+ if ( bPressed )
+ {
+ if ( m_bGotMouseButtonDown &&
+ ( (int)( event.button.timestamp - m_MouseButtonDownTimeStamp ) <= sdl_double_click_time.GetInt() ) &&
+ ( abs( event.button.x - m_MouseButtonDownX ) <= sdl_double_click_size.GetInt() ) &&
+ ( abs( event.button.y - m_MouseButtonDownY ) <= sdl_double_click_size.GetInt() ) )
+ {
+ bDoublePress = true;
+ m_bGotMouseButtonDown = false;
+ }
+ else
+ {
+ m_MouseButtonDownTimeStamp = event.button.timestamp;
+ m_MouseButtonDownX = event.button.x;
+ m_MouseButtonDownY = event.button.y;
+ m_bGotMouseButtonDown = true;
+ }
+ }
+
+ CCocoaEvent theEvent;
+ theEvent.m_EventType = (bPressed) ? CocoaEvent_MouseButtonDown : CocoaEvent_MouseButtonUp;
+ theEvent.m_MousePos[0] = event.button.x * m_flMouseXScale;
+ theEvent.m_MousePos[1] = event.button.y * m_flMouseYScale;
+ theEvent.m_MouseButtonFlags = m_mouseButtons;
+ theEvent.m_nMouseClickCount = bDoublePress ? 2 : 1;
+ theEvent.m_MouseButton = cocoaButton;
+ PostEvent( theEvent );
+
+ break;
+ }
+
+ case SDL_MOUSEWHEEL:
+ {
+ if ( event.wheel.y )
+ {
+ CCocoaEvent theEvent;
+ theEvent.m_EventType = CocoaEvent_MouseScroll;
+ const int scroll = event.wheel.y;
+ theEvent.m_MousePos[0] = scroll;
+ theEvent.m_MousePos[1] = scroll;
+ PostEvent( theEvent );
+ }
+ break;
+ }
+
+ case SDL_WINDOWEVENT:
+ switch (event.window.event)
+ {
+ case SDL_WINDOWEVENT_EXPOSED:
+ {
+ /*if ( ev.xexpose.count > 0 )
+ break; // multiple expose events queued
+ EVENT_LOG( "Got event Expose\n" );
+ int iPanel = m_mapWindowToVPanel.Find( ev.xexpose.window );
+ if ( iPanel != m_mapWindowToVPanel.InvalidIndex() )
+ drawVGUI( m_pXDisplay, ev.xexpose.window, m_mapWindowToVPanel[ iPanel ], m_GLContext );
+ m_mapSentInvalidate.RemoveAll();*/
+ break;
+ }
+ case SDL_WINDOWEVENT_FOCUS_GAINED:
+ {
+ m_bHasFocus = true;
+ SDL_ShowCursor( m_bCursorVisible ? 1 : 0 );
+ CCocoaEvent theEvent;
+ theEvent.m_EventType = CocoaEvent_AppActivate;
+ theEvent.m_ModifierKeyMask = 1;
+ PostEvent( theEvent );
+ break;
+ }
+ case SDL_WINDOWEVENT_FOCUS_LOST:
+ {
+ m_bHasFocus = false;
+ SDL_ShowCursor(1);
+ CCocoaEvent theEvent;
+ theEvent.m_EventType = CocoaEvent_AppActivate;
+ theEvent.m_ModifierKeyMask = 0;
+ PostEvent( theEvent );
+ break;
+ }
+ case SDL_WINDOWEVENT_LEAVE:
+ {
+ if ( !IsWindows() && !m_bRawInput && !m_bCursorVisible && m_bHasFocus )
+ {
+ // If the cursor is not visible and the mouse
+ // cursor somehow manages to escape the window
+ // warp it back to the middle of the window.
+ SDL_WarpMouseInWindow( m_Window, m_nMouseTargetX, m_nMouseTargetY );
+ m_bExpectSyntheticMouseMotion = true;
+ }
+ break;
+ }
+ }
+ break;
+
+ case SDL_KEYUP:
+ case SDL_KEYDOWN:
+ handleKeyInput(event);
+ break;
+
+ case SDL_TEXTINPUT:
+ {
+ char *text = event.text.text;
+
+ if ( text && text[ 0 ] )
+ {
+ wchar_t WBuf[ SDL_TEXTINPUTEVENT_TEXT_SIZE + 1 ];
+ WBuf[ 0 ] = 0;
+ V_UTF8ToUnicode( text, WBuf, sizeof( WBuf ) );
+
+ for ( int i = 0; i < SDL_TEXTINPUTEVENT_TEXT_SIZE; i++ )
+ {
+ wchar_t ch = WBuf[ i ];
+ if ( ch == '\0' )
+ break;
+
+ CCocoaEvent theEvent;
+ theEvent.m_EventType = CocoaEvent_KeyDown;
+ theEvent.m_VirtualKeyCode = 0;
+ theEvent.m_UnicodeKey = ch;
+ theEvent.m_UnicodeKeyUnmodified = ch;
+ theEvent.m_ModifierKeyMask = m_keyModifierMask;
+ PostEvent( theEvent, false );
+
+ theEvent.m_EventType = CocoaEvent_KeyUp;
+ theEvent.m_VirtualKeyCode = 0;
+ theEvent.m_UnicodeKey = 0;
+ theEvent.m_UnicodeKeyUnmodified = 0;
+ theEvent.m_ModifierKeyMask = m_keyModifierMask;
+ PostEvent( theEvent, false );
+ }
+ }
+ break;
+ }
+
+ case SDL_QUIT:
+ {
+ CCocoaEvent theEvent;
+ theEvent.m_EventType = CocoaEvent_AppQuit;
+ PostEvent( theEvent );
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+}
+
+void CSDLMgr::IncWindowRefCount()
+{
+ if ( !m_Window )
+ return;
+
+ m_nWindowRefCount++;
+}
+
+void CSDLMgr::DecWindowRefCount()
+{
+ if ( !m_Window )
+ return;
+
+ Assert( m_nWindowRefCount >= 1 );
+ if ( !m_nWindowRefCount )
+ return;
+
+ m_nWindowRefCount--;
+
+ if ( !m_nWindowRefCount )
+ {
+#if defined( DX_TO_GL_ABSTRACTION )
+ if ( m_Window )
+ {
+ SDL_GL_MakeCurrent( m_Window, m_GLContext );
+ }
+
+ if ( gGL && m_readFBO )
+ {
+ gGL->glDeleteFramebuffersEXT( 1, &m_readFBO );
+ }
+ m_readFBO = 0;
+
+ SDL_GL_DeleteContext( m_GLContext );
+#if !defined( OSX ) && defined( DBGFLAG_ASSERT )
+ // Clear the GL entrypoint pointers, ensuring we crash if someone tries to call GL after we delete the context.
+ Msg( "%s: Calling ClearOpenGLEntryPoints. Should crash if someone calls GL after this.\n", __FUNCTION__ );
+ ClearOpenGLEntryPoints();
+#endif
+
+ m_GLContext = NULL;
+#endif // DX_TO_GL_ABSTRACTION
+
+ SDL_SetWindowFullscreen(m_Window, SDL_FALSE); // just in case.
+ SDL_SetWindowGrab(m_Window, SDL_FALSE); // just in case.
+ SDL_DestroyWindow(m_Window);
+ m_Window = NULL;
+ SetAssertDialogParent( NULL );
+ }
+}
+
+void CSDLMgr::DestroyGameWindow()
+{
+ SDLAPP_FUNC;
+
+ if ( m_Window )
+ {
+ DecWindowRefCount();
+ }
+}
+
+void CSDLMgr::SetApplicationIcon( const char *pchAppIconFile )
+{
+ SDLAPP_FUNC;
+
+ SDL_Surface *icon = SDL_LoadBMP(pchAppIconFile);
+ if (icon)
+ {
+ SDL_SetWindowIcon(m_Window, icon);
+ SDL_FreeSurface(icon);
+ }
+}
+
+void CSDLMgr::GetMouseDelta( int &x, int &y, bool bIgnoreNextMouseDelta )
+{
+ SDLAPP_FUNC;
+
+ x = m_nMouseXDelta;
+ y = m_nMouseYDelta;
+
+ m_nMouseXDelta = m_nMouseYDelta = 0;
+}
+
+// Returns the resolution of the nth display. 0 is the default display.
+//
+void CSDLMgr::GetNativeDisplayInfo( int nDisplay, uint &nWidth, uint &nHeight, uint &nRefreshHz )
+{
+ SDL_DisplayMode mode;
+
+ if ( nDisplay == -1 )
+ {
+ if ( g_bSDLDisplayindexSet )
+ {
+ nDisplay = sdl_displayindex.GetInt();
+ }
+ else
+ {
+ // sdl_displayindex hasn't been parsed yet. This can happen in CMaterialSystem::ModInit()
+ // before the config files have been read, etc. So in this case, just grab the largest
+ // display we can find and return with that.
+ int Width, Height;
+ nDisplay = GetLargestDisplaySize( Width, Height );
+ }
+ }
+
+ if ( SDL_GetDesktopDisplayMode( nDisplay, &mode ) != 0 )
+ {
+ Assert( 0 );
+ SDL_GetDesktopDisplayMode( 0, &mode );
+ }
+
+ nRefreshHz = mode.refresh_rate;
+ nWidth = mode.w;
+ nHeight = mode.h;
+}
+
+
+void CSDLMgr::RenderedSize( uint &width, uint &height, bool set )
+{
+ SDLAPP_FUNC;
+
+ if (set)
+ {
+ m_renderedWidth = width;
+ m_rendererHeight = height; // latched from NotifyRenderedSize
+ }
+ else
+ {
+ width = m_renderedWidth;
+ height = m_rendererHeight;
+ }
+}
+
+void CSDLMgr::DisplayedSize( uint &width, uint &height )
+{
+ SDLAPP_FUNC;
+
+ int w, h;
+ SDL_GetWindowSize(m_Window, &w, &h);
+ width = (uint) w;
+ height = (uint) h;
+}
+
+void CSDLMgr::GetStackCrawl( CStackCrawlParams *params )
+{
+ SDLAPP_FUNC;
+}
+
+void CSDLMgr::WaitUntilUserInput( int msSleepTime )
+{
+ SDLAPP_FUNC;
+
+ SDL_WaitEventTimeout(NULL, msSleepTime);
+}
+
+//===============================================================================
+
+void CSDLMgr::SetGammaRamp( const uint16 *pRed, const uint16 *pGreen, const uint16 *pBlue )
+{
+ if ( m_Window )
+ {
+ int nResult = SDL_SetWindowGammaRamp( m_Window, pRed, pGreen, pBlue );
+
+ if ( nResult != 0 )
+ {
+ ConMsg( "SDL_SetWindowGammaRamp failed: %d\n", nResult );
+ }
+ }
+}
+
+//===============================================================================
+
+#if defined( DX_TO_GL_ABSTRACTION )
+void CSDLMgr::GetDesiredPixelFormatAttribsAndRendererInfo( uint **ptrOut, uint *countOut, GLMRendererInfoFields *rendInfoOut )
+{
+ SDLAPP_FUNC;
+
+ Assert( m_pixelFormatAttribCount > 0 );
+
+ if (ptrOut) *ptrOut = (uint *) m_pixelFormatAttribs;
+ if (countOut) *countOut = m_pixelFormatAttribCount;
+ if (rendInfoOut)
+ {
+ GLMDisplayDB *db = GetDisplayDB();
+#ifdef OSX
+ *rendInfoOut = db->m_renderers->Head()->m_info;
+#else
+ *rendInfoOut = db->m_renderer.m_info;
+#endif
+ }
+}
+
+
+
+GLMDisplayMode::GLMDisplayMode( uint width, uint height, uint refreshHz )
+{
+ SDLAPP_FUNC;
+
+ Init( width, height, refreshHz );
+}
+
+GLMDisplayMode::~GLMDisplayMode()
+{
+ SDLAPP_FUNC;
+ // empty
+}
+
+void GLMDisplayMode::Init( uint width, uint height, uint refreshHz )
+{
+ SDLAPP_FUNC;
+
+ m_info.m_modePixelWidth = width;
+ m_info.m_modePixelHeight = height;
+ m_info.m_modeRefreshHz = refreshHz;
+}
+
+void GLMDisplayMode::Dump( int which )
+{
+ SDLAPP_FUNC;
+
+ GLMPRINTF(("\n # %-2d width=%-4d height=%-4d refreshHz=%-2d",
+ which, m_info.m_modePixelWidth, m_info.m_modePixelHeight, m_info.m_modeRefreshHz ));
+}
+
+GLMDisplayDB *CSDLMgr::GetDisplayDB( void )
+{
+ SDLAPP_FUNC;
+
+ if ( !m_displayDB )
+ {
+ m_displayDB = new GLMDisplayDB; // creating the DB object does not do much other than init it to a good state.
+ m_displayDB->Populate(); // populate the tree
+#if defined( OSX )
+ // side effect: we fill in m_force_vsync..
+ {
+ GLMRendererInfoFields info;
+ m_displayDB->GetRendererInfo( 0, &info );
+
+ // m_leopard = (info.m_osComboVersion < 0x000A0600);
+
+ m_force_vsync = info.m_badDriver1064NV; // just force it if it's the bum NV driver
+ }
+#endif
+ }
+ return m_displayDB;
+}
+
+#ifndef OSX
+# include "glmdisplaydb_linuxwin.inl"
+#endif
+
+
+#endif // DX_TO_GL_ABSTRACTION
+
+#endif // !DEDICATED
+