diff options
Diffstat (limited to 'appframework')
| -rw-r--r-- | appframework/AppSystemGroup.cpp | 573 | ||||
| -rw-r--r-- | appframework/VguiMatSysApp.cpp | 308 | ||||
| -rw-r--r-- | appframework/WinApp.cpp | 248 | ||||
| -rw-r--r-- | appframework/appframework.vpc | 53 | ||||
| -rw-r--r-- | appframework/glmdisplaydb_linuxwin.inl | 630 | ||||
| -rw-r--r-- | appframework/glmrendererinfo_osx.mm | 1583 | ||||
| -rw-r--r-- | appframework/posixapp.cpp | 182 | ||||
| -rw-r--r-- | appframework/sdlmgr.cpp | 2078 |
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 + |