csgo-2018-source/game/client/vgui_int.cpp
2021-07-24 21:11:47 -07:00

1044 lines
27 KiB
C++

//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "vgui_int.h"
#include "ienginevgui.h"
#include "itextmessage.h"
#include "vguicenterprint.h"
#include "iloadingdisc.h"
#include "ifpspanel.h"
#include "imessagechars.h"
#include "inetgraphpanel.h"
#include "idebugoverlaypanel.h"
#include <vgui/ISurface.h>
#include <vgui/IVGui.h>
#include <vgui/IInput.h>
#include "tier0/vprof.h"
#include "iclientmode.h"
#include <vgui_controls/Panel.h>
#include <keyvalues.h>
#include "filesystem.h"
#include "matsys_controls/matsyscontrols.h"
#ifdef SIXENSE
#include "sixense/in_sixense.h"
#endif
#ifdef _PS3
#include "ps3/ps3_core.h"
#endif
using namespace vgui;
#ifndef _GAMECONSOLE
void MP3Player_Create( vgui::VPANEL parent );
void MP3Player_Destroy();
#endif
#include <vgui/IInputInternal.h>
vgui::IInputInternal *g_InputInternal = NULL;
#include <vgui_controls/Controls.h>
#include "cstrike15/gameui/cstrike15/steamoverlay/isteamoverlaymgr.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
bool IsWidescreen( void );
void ss_pipsplit_changed( IConVar *pConVar, const char *pOldString, float flOldValue )
{
VGui_OnSplitScreenStateChanged();
}
static ConVar ss_pipsplit( "ss_pipsplit", "1", 0, "If enabled, use PIP instead of splitscreen. (Only works for 2 players)", ss_pipsplit_changed );
static ConVar ss_pipscale( "ss_pipscale", "0.3f", 0, "Scale of the PIP aspect ratio to our resolution.", ss_pipsplit_changed );
static ConVar ss_pip_right_offset( "ss_pip_right_offset", "25", 0, "PIP offset vector from the right of the screen", ss_pipsplit_changed );
static ConVar ss_pip_bottom_offset( "ss_pip_bottom_offset", "25", 0, "PIP offset vector from the bottom of the screen", ss_pipsplit_changed );
static ConVar ss_force_primary_fullscreen( "ss_force_primary_fullscreen", "0", 0, "If enabled, all splitscreen users will only see the first user's screen full screen", ss_pipsplit_changed );
bool VGui_UsePipSplit();
void ss_verticalsplit_changed( IConVar *pConVar, const char *pOldString, float flOldValue )
{
ConVarRef var( pConVar );
if ( var.GetBool() != !!(int)flOldValue )
{
VGui_OnSplitScreenStateChanged();
if ( GetFullscreenClientMode() )
{
// we have to force re-layout, because the screen dimensions haven't changed,
// but our layout is going to be different.
GetFullscreenClientMode()->Layout( true );
}
FOR_EACH_VALID_SPLITSCREEN_PLAYER( i )
{
ACTIVE_SPLITSCREEN_PLAYER_GUARD_VGUI( i );
GetClientMode()->Layout();
GetHud().OnSplitScreenStateChanged();
}
}
}
static ConVar ss_verticalsplit( "ss_verticalsplit", "0", 0, "Two player split screen uses vertical split (do not set this directly, use ss_splitmode instead).", ss_verticalsplit_changed );
void ss_splitmode_changed( IConVar *pConVar, const char *pOldString, float flOldValue )
{
ConVarRef var( pConVar );
if ( !IsWidescreen() )
{
// Non-widescreen is alway horizontal
ss_verticalsplit.SetValue( 0 );
}
else
{
if ( var.GetInt() == 1 )
{
// Horizontal
ss_verticalsplit.SetValue( 0 );
}
else if ( var.GetInt() == 2 )
{
// Vertical
ss_verticalsplit.SetValue( 1 );
}
else
{
// Vertical is default for widescreen
ss_verticalsplit.SetValue( 1 );
}
}
}
static ConVar ss_splitmode( "ss_splitmode", "0", FCVAR_ARCHIVE | FCVAR_ARCHIVE_GAMECONSOLE, "Two player split screen mode (0 - recommended settings base on the width, 1 - horizontal, 2 - vertical (only allowed in widescreen)", ss_splitmode_changed );
static ConVar ss_enable( "ss_enable", "0", FCVAR_RELEASE, "Enables Split Screen support. Play Single Player now launches into split screen mode. NO ONLINE SUPPORT" );
void GetVGUICursorPos( int& x, int& y )
{
vgui::input()->GetCursorPos(x, y);
}
void SetVGUICursorPos( int x, int y )
{
if ( !g_bTextMode )
{
vgui::input()->SetCursorPos(x, y);
}
}
class CHudTextureHandleProperty : public vgui::IPanelAnimationPropertyConverter
{
public:
virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
{
void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
CHudTextureHandle *pHandle = ( CHudTextureHandle * )data;
// lookup texture name for id
if ( pHandle->Get() )
{
kv->SetString( entry->name(), pHandle->Get()->szShortName );
}
else
{
kv->SetString( entry->name(), "" );
}
}
virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
{
void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
CHudTextureHandle *pHandle = ( CHudTextureHandle * )data;
const char *texturename = kv->GetString( entry->name() );
if ( texturename && texturename[ 0 ] )
{
CHudTexture *currentTexture = HudIcons().GetIcon( texturename );
pHandle->Set( currentTexture );
}
else
{
pHandle->Set( NULL );
}
}
virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry )
{
void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
CHudTextureHandle *pHandle = ( CHudTextureHandle * )data;
const char *texturename = entry->defaultvalue();
if ( texturename && texturename[ 0 ] )
{
CHudTexture *currentTexture = HudIcons().GetIcon( texturename );
pHandle->Set( currentTexture );
}
else
{
pHandle->Set( NULL );
}
}
};
class CSplitScreenLetterBox
{
public:
enum
{
SPLITSCREEN_NONWIDESCREEN_HORIZONTAL_SPLIT = 0,
SPLITSCREEN_WIDESCREEN_HORIZONTAL_SPLIT,
SPLITSCREEN_WIDESCREEN_VERTICAL_SPLIT,
NUM_SPLITSCREEN_TYPES,
};
void Init();
void SetNumSplitScreenPlayers( int nPlayers );
bool GetSettings( bool *pbInsetHud, float *pflAspect, float *pFOV, float *pViewmodelFOV );
private:
struct LetterBox_t
{
LetterBox_t() : m_flAspectRatio( 4.0f / 3.0f ), m_bInsetHud( false ) {}
float m_flAspectRatio;
bool m_bInsetHud;
float m_flFOV;
float m_flViewModelFOV;
};
bool m_bValid;
LetterBox_t m_Settings[ NUM_SPLITSCREEN_TYPES ];
int m_nSplitScreenPlayers;
};
void CSplitScreenLetterBox::Init()
{
m_nSplitScreenPlayers = 1;
char const *pchSlotNames[] = { "nonwidescreen", "widescreen_horizontal_split", "widescreen_vertical_split" };
char const *pchConfigFile = "splitscreen_config.txt";
m_bValid = true;
KeyValues *kv = new KeyValues( "splitscreen" );
if ( kv->LoadFromFile( g_pFullFileSystem, pchConfigFile, "MOD" ) )
{
for ( int i = 0; i < NUM_SPLITSCREEN_TYPES && m_bValid; ++i )
{
KeyValues *settings = kv->FindKey( pchSlotNames[ i ], false );
if ( settings )
{
// Get settings
char const *pchAspect = settings->GetString( "aspect", "4 by 3" );
if ( pchAspect )
{
// Allowable syntax is "16 by 9" or "16 x 9" or "1.77"
if ( Q_stristr( pchAspect, " by " ) )
{
float f1, f2;
if ( 2 == sscanf( pchAspect, "%f by %f", &f1, &f2 ) && f2 > 0.001f )
{
m_Settings[ i ].m_flAspectRatio = f1 / f2;
}
else
{
Error( "%s: Invalid aspect ratio string '%s'\n", pchConfigFile, pchAspect );
m_bValid = false;
}
}
else if ( Q_stristr( pchAspect, " x " ) )
{
float f1, f2;
if ( 2 == sscanf( pchAspect, "%f x %f", &f1, &f2 ) && f2 > 0.001f )
{
m_Settings[ i ].m_flAspectRatio = f1 / f2;
}
else
{
Error( "%s: Invalid aspect ratio string '%s'\n", pchConfigFile, pchAspect );
m_bValid = false;
}
}
else if ( Q_atof( pchAspect ) > 0.1f )
{
m_Settings[ i ].m_flAspectRatio = Q_atof( pchAspect );
}
else
{
Error( "%s: Invalid aspect ratio string '%s'\n", pchConfigFile, pchAspect );
m_bValid = false;
}
}
// Get inset for hud
m_Settings[ i ].m_bInsetHud = settings->GetBool( "insethud", false );
// Get FOV
m_Settings[ i ].m_flFOV = settings->GetFloat( "fov", 90.0f );
// Get viewmodel FOVs
m_Settings[ i ].m_flViewModelFOV = settings->GetFloat( "viewmodelfov", 50.0f );
}
else
{
Error( "%s: Missing settings block for split screen mode '%s'\n", pchConfigFile, pchSlotNames[ i ] );
m_bValid = false;
break;
}
}
}
else
{
Msg( "No split screen config file '%s', using defaults\n", pchConfigFile );
m_bValid = false;
}
kv->deleteThis();
}
void CSplitScreenLetterBox::SetNumSplitScreenPlayers( int nPlayers )
{
m_nSplitScreenPlayers = nPlayers;
}
bool IsWidescreen( void )
{
const AspectRatioInfo_t &aspectRatioInfo = materials->GetAspectRatioInfo();
return aspectRatioInfo.m_bIsWidescreen;
}
bool CSplitScreenLetterBox::GetSettings( bool *pbInsetHud, float *pflAspect, float *pFOV, float *pViewModelFOV )
{
Assert( pbInsetHud );
Assert( pflAspect );
Assert( pFOV );
Assert( pViewModelFOV );
static bool bUsedDefaultsLastTime = false;
if ( !m_bValid || m_nSplitScreenPlayers == 1 || VGui_UsePipSplit() || ss_force_primary_fullscreen.GetBool() )
{
if ( !bUsedDefaultsLastTime )
{
bUsedDefaultsLastTime = true;
}
*pbInsetHud = false;
*pflAspect = 4.0f / 3.0f;
// FIXME: These are the non-splitscreen defaults for L4D. This code needs to be sanitized for other games.
*pFOV = 90.0f;
*pViewModelFOV = 50.0f;
return false;
}
// Figure out which splitscreen mode to use based on current configuration.
int slot;
if ( IsWidescreen() )
{
if ( ss_verticalsplit.GetBool() )
{
slot = SPLITSCREEN_WIDESCREEN_VERTICAL_SPLIT;
}
else
{
slot = SPLITSCREEN_WIDESCREEN_HORIZONTAL_SPLIT;
}
}
else
{
slot = SPLITSCREEN_NONWIDESCREEN_HORIZONTAL_SPLIT;
}
bUsedDefaultsLastTime = false;
const LetterBox_t &lb = m_Settings[ slot ];
*pbInsetHud = lb.m_bInsetHud;
*pflAspect = lb.m_flAspectRatio;
*pFOV = lb.m_flFOV;
*pViewModelFOV = lb.m_flViewModelFOV;
return true;
}
static CSplitScreenLetterBox g_LetterBox;
CON_COMMAND( ss_reloadletterbox, "ss_reloadletterbox" )
{
g_LetterBox.Init();
VGui_OnSplitScreenStateChanged();
FOR_EACH_VALID_SPLITSCREEN_PLAYER( i )
{
ACTIVE_SPLITSCREEN_PLAYER_GUARD_VGUI( i );
GetClientMode()->Layout();
GetHud().OnSplitScreenStateChanged();
}
}
static CHudTextureHandleProperty textureHandleConverter;
static void VGui_OneTimeInit()
{
static bool initialized = false;
if ( initialized )
return;
initialized = true;
vgui::Panel::AddPropertyConverter( "CHudTextureHandle", &textureHandleConverter );
g_LetterBox.Init();
}
bool VGui_Startup( CreateInterfaceFn appSystemFactory )
{
if ( !vgui::VGui_InitInterfacesList( "CLIENT", &appSystemFactory, 1 ) )
return false;
if ( !vgui::VGui_InitMatSysInterfacesList( "CLIENT", &appSystemFactory, 1 ) )
return false;
g_InputInternal = (IInputInternal *)appSystemFactory( VGUI_INPUTINTERNAL_INTERFACE_VERSION, NULL );
if ( !g_InputInternal )
{
return false; // c_vguiscreen.cpp needs this!
}
VGui_OneTimeInit();
// Create any root panels for .dll
VGUI_CreateClientDLLRootPanel();
// Make sure we have a panel
for ( int hh = 0; hh < MAX_SPLITSCREEN_PLAYERS; ++hh )
{
ACTIVE_SPLITSCREEN_PLAYER_GUARD_VGUI( hh );
VPANEL root = VGui_GetClientDLLRootPanel();
if ( !root )
{
return false;
}
}
CUtlVector< Panel * > list;
VGui_GetPanelList( list );
for ( int i = 0; i < list.Count(); ++i )
{
list[ i ]->SetMessageContextId_R( (uint32)i );
}
VGui_GetFullscreenRootPanel()->SetMessageContextId_R( (uint32)0 );
VGui_OnSplitScreenStateChanged();
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void VGui_CreateGlobalPanels( void )
{
VPANEL gameToolParent = enginevgui->GetPanel( PANEL_CLIENTDLL_TOOLS );
VPANEL toolParent = enginevgui->GetPanel( PANEL_TOOLS );
#if defined( TRACK_BLOCKING_IO )
VPANEL gameDLLPanel = enginevgui->GetPanel( PANEL_GAMEDLL );
#endif
// Part of game
for ( int hh = 0; hh < MAX_SPLITSCREEN_PLAYERS; ++hh )
{
ACTIVE_SPLITSCREEN_PLAYER_GUARD_VGUI( hh );
VPANEL root = VGui_GetClientDLLRootPanel();
GetCenterPrint()->Create( root );
}
loadingdisc->Create( gameToolParent );
messagechars->Create( gameToolParent );
// Debugging or related tool
fps->Create( toolParent );
#if defined( TRACK_BLOCKING_IO )
iopanel->Create( gameDLLPanel );
#endif
netgraphpanel->Create( toolParent );
debugoverlaypanel->Create( gameToolParent );
#ifndef _GAMECONSOLE
// Create mp3 player off of tool parent panel
MP3Player_Create( toolParent );
#endif
// Create Steam overlay
if ( IsPS3() && g_pISteamOverlayMgr )
g_pISteamOverlayMgr->Create( enginevgui->GetPanel( PANEL_STEAMOVERLAY ) );
#ifdef SIXENSE
g_pSixenseInput->CreateGUI( gameToolParent );
#endif
}
void VGui_Shutdown()
{
// Destroy Steam overlay
if ( IsPS3() && g_pISteamOverlayMgr )
g_pISteamOverlayMgr->Destroy();
#ifndef _GAMECONSOLE
MP3Player_Destroy();
#endif
netgraphpanel->Destroy();
debugoverlaypanel->Destroy();
#if defined( TRACK_BLOCKING_IO )
iopanel->Destroy();
#endif
fps->Destroy();
messagechars->Destroy();
loadingdisc->Destroy();
for ( int hh = 0; hh < MAX_SPLITSCREEN_PLAYERS; ++hh )
{
ACTIVE_SPLITSCREEN_PLAYER_GUARD_VGUI( hh );
GetCenterPrint()->Destroy();
}
for ( int hh = 0; hh < MAX_SPLITSCREEN_PLAYERS; ++hh )
{
ACTIVE_SPLITSCREEN_PLAYER_GUARD_VGUI( hh );
if ( GetClientMode() )
{
GetClientMode()->VGui_Shutdown();
if ( hh == 0 )
{
GetFullscreenClientMode()->VGui_Shutdown();
}
}
}
VGUI_DestroyClientDLLRootPanel();
// Make sure anything "marked for deletion"
// actually gets deleted before this dll goes away
vgui::ivgui()->RunFrame();
}
static ConVar cl_showpausedimage( "cl_showpausedimage", "1", 0, "Show the 'Paused' image when game is paused." );
//-----------------------------------------------------------------------------
// Things to do before rendering vgui stuff...
//-----------------------------------------------------------------------------
void VGui_PreRender()
{
ASSERT_LOCAL_PLAYER_RESOLVABLE();
VPROF( "VGui_PreRender" );
// 360 does not use these plaques
#if !defined( PORTAL2 )
if ( IsPC() )
{
loadingdisc->SetLoadingVisible( engine->IsDrawingLoadingImage() && !engine->IsPlayingDemo() );
loadingdisc->SetPausedVisible( !enginevgui->IsGameUIVisible() && cl_showpausedimage.GetBool() && engine->IsPaused() && !engine->IsTakingScreenshot() && !engine->IsPlayingDemo() );
}
#endif
int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
CUtlVector< Panel * > list;
VGui_GetPanelList( list );
for ( int i = 0; i < list.Count() ; ++i )
{
list[ i ]->SetVisible( i == nSlot );
}
VGui_GetFullscreenRootPanel()->SetVisible( true );
}
void VGui_PostRender()
{
int w, h;
CUtlVector< Panel * > list;
VGui_GetPanelList( list );
FOR_EACH_VALID_SPLITSCREEN_PLAYER( i )
{
int x, y;
VGui_GetHudBounds( i, x, y, w, h);
list[ i ]->SetVisible( true );
list[ i ]->SetBounds( x, y, w, h );
surface()->SetAbsPosForContext( i, x, y );
}
VGui_GetTrueScreenSize( w, h );
VGui_GetFullscreenRootPanel()->SetVisible( true );
VGui_GetFullscreenRootPanel()->SetBounds( 0, 0, w, h );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : cl_panelanimation -
//-----------------------------------------------------------------------------
CON_COMMAND( cl_panelanimation, "Shows panel animation variables: <panelname | blank for all panels>." )
{
if ( args.ArgC() == 2 )
{
PanelAnimationDumpVars( args[1] );
}
else
{
PanelAnimationDumpVars( NULL );
}
}
void GetHudSize( int& w, int &h )
{
vgui::surface()->GetScreenSize( w, h );
}
static vrect_t g_TrueScreenSize;
static vrect_t g_ScreenSpaceBounds[ MAX_SPLITSCREEN_CLIENTS ];
void VGui_GetTrueScreenSize( int &w, int &h )
{
w = g_TrueScreenSize.width;
h = g_TrueScreenSize.height;
}
void VGUI_SetScreenSpaceBounds( int slot, int x, int y, int w, int h )
{
vrect_t &r = g_ScreenSpaceBounds[ slot ];
r.x = x;
r.y = y;
r.width = w;
r.height = h;
}
void VGUI_UpdateScreenSpaceBounds( int nNumSplits, int sx, int sy, int sw, int sh )
{
g_TrueScreenSize.x = sx;
g_TrueScreenSize.y = sy;
g_TrueScreenSize.width = sw;
g_TrueScreenSize.height = sh;
CUtlVector< int > validSlots;
FOR_EACH_VALID_SPLITSCREEN_PLAYER( i )
{
validSlots.AddToTail( i );
}
Assert( validSlots.Count() == nNumSplits );
switch ( nNumSplits )
{
default:
case 1:
// Make it screen sized
{
VGUI_SetScreenSpaceBounds( validSlots[ 0 ], sx, sy, sw, sh );
}
break;
case 2:
{
if ( ss_force_primary_fullscreen.GetBool() )
{
// fullscreen
VGUI_SetScreenSpaceBounds( validSlots[ 0 ], 0, 0, sw, sh );
VGUI_SetScreenSpaceBounds( validSlots[ 1 ], sw, sh, 1, 1 );
}
else if ( VGui_UsePipSplit() )
{
VGUI_SetScreenSpaceBounds( validSlots[ 0 ], sx, sy, sw, sh );
// scale with PIP resolution
float flPIPScale = ss_pipscale.GetFloat();
int pipWidth = sw * flPIPScale;
int pipHeight = sh * flPIPScale;
int x = sw - pipWidth - ss_pip_right_offset.GetInt();
int y = sh - pipHeight - ss_pip_bottom_offset.GetInt();
// round upper left corner down to the nearest multiple of 8 for X360 (resolve alignment requirements)
if ( IsX360() )
{
x &= (~7);
y &= (~7);
}
VGUI_SetScreenSpaceBounds( validSlots[ 1 ], x, y, pipWidth, pipHeight );
}
else if ( ss_verticalsplit.GetBool() )
{
sw /= 2;
// Stack two horiz, side by side
VGUI_SetScreenSpaceBounds( validSlots[ 0 ], sx, sy, sw, sh );
VGUI_SetScreenSpaceBounds( validSlots[ 1 ], sx + sw, sy, sw, sh );
}
else
{
sh /= 2;
// Stack two wide on top of one another
VGUI_SetScreenSpaceBounds( validSlots[ 0 ], sx, sy, sw, sh );
VGUI_SetScreenSpaceBounds( validSlots[ 1 ], sx, sy + sh, sw, sh );
}
}
break;
case 3:
{
int fullw = sw;
sw /= 2;
sh /= 2;
VGUI_SetScreenSpaceBounds( validSlots[ 0 ], sx + ( fullw - sw ) / 2, sy, sw, sh );
VGUI_SetScreenSpaceBounds( validSlots[ 1 ], sx, sy + sh, sw, sh );
VGUI_SetScreenSpaceBounds( validSlots[ 2 ], sx + sw, sy + sh, sw, sh );
}
break;
case 4:
{
sw /= 2;
sh /= 2;
// Stack two wide on top of one another
VGUI_SetScreenSpaceBounds( validSlots[ 0 ], sx, sy, sw, sh );
VGUI_SetScreenSpaceBounds( validSlots[ 1 ], sx + sw, sy, sw, sh );
VGUI_SetScreenSpaceBounds( validSlots[ 2 ], sx, sy + sh, sw, sh );
VGUI_SetScreenSpaceBounds( validSlots[ 3 ], sx + sw, sy + sh, sw, sh );
}
break;
}
}
CBitVec< MAX_SPLITSCREEN_PLAYERS > g_SplitScreenPlayers;
bool g_bIterateRemoteSplitScreenPlayers = false;
C_BasePlayer *g_RemoteSplitScreenPlayers[MAX_SPLITSCREEN_PLAYERS];
void AddRemoteSplitScreenViewPlayer( C_BasePlayer *pPlayer )
{
for( int i = 0; i != MAX_SPLITSCREEN_PLAYERS; ++i )
{
if( g_RemoteSplitScreenPlayers[i] == pPlayer )
return; //don't add it twice
}
for( int i = 0; i != MAX_SPLITSCREEN_PLAYERS; ++i )
{
if( !g_SplitScreenPlayers.IsBitSet( i ) && (g_RemoteSplitScreenPlayers[i] == NULL) )
{
g_RemoteSplitScreenPlayers[i] = pPlayer;
VGui_OnSplitScreenStateChanged();
return;
}
}
}
void RemoveRemoteSplitScreenViewPlayer( C_BasePlayer *pPlayer )
{
for( int i = 0; i != MAX_SPLITSCREEN_PLAYERS; ++i )
{
if( g_RemoteSplitScreenPlayers[i] == pPlayer )
{
g_RemoteSplitScreenPlayers[i] = NULL;
VGui_OnSplitScreenStateChanged();
return;
}
}
}
C_BasePlayer *GetSplitScreenViewPlayer( int nSlot )
{
return g_SplitScreenPlayers.IsBitSet( nSlot ) ? C_BasePlayer::GetLocalPlayer( nSlot ) : g_RemoteSplitScreenPlayers[nSlot];
}
void cl_enable_remote_splitscreen_callback_f( IConVar *var, const char *pOldValue, float flOldValue )
{
VGui_OnSplitScreenStateChanged();
}
ConVar cl_enable_remote_splitscreen( "cl_enable_remote_splitscreen", "0", 0, "Allows viewing of nonlocal players in a split screen fashion", cl_enable_remote_splitscreen_callback_f );
static CUtlVector<bool> s_IterateNetworkedSplitScreenSlotsPushedValues;
void IterateRemoteSplitScreenViewSlots_Push( bool bSet )
{
if( !cl_enable_remote_splitscreen.GetBool() )
{
bSet = false;
}
s_IterateNetworkedSplitScreenSlotsPushedValues.AddToTail( g_bIterateRemoteSplitScreenPlayers );
g_bIterateRemoteSplitScreenPlayers = bSet;
}
void IterateRemoteSplitScreenViewSlots_Pop( void )
{
Assert( s_IterateNetworkedSplitScreenSlotsPushedValues.Count() > 0 );
g_bIterateRemoteSplitScreenPlayers = s_IterateNetworkedSplitScreenSlotsPushedValues.Tail();
s_IterateNetworkedSplitScreenSlotsPushedValues.RemoveMultipleFromTail( 1 );
}
bool IsLocalSplitScreenPlayer( int nSlot )
{
return g_SplitScreenPlayers.IsBitSet( nSlot );
}
int FirstValidSplitScreenSlot()
{
return 0;
}
int NextValidSplitScreenSlot( int i )
{
++i;
while ( i< MAX_SPLITSCREEN_PLAYERS )
{
if ( g_SplitScreenPlayers.IsBitSet( i ) )
return i;
if( g_bIterateRemoteSplitScreenPlayers && cl_enable_remote_splitscreen.GetBool() && (g_RemoteSplitScreenPlayers[i] != NULL) )
return i;
++i;
}
return -1;
}
bool IsValidSplitScreenSlot( int i )
{
return g_SplitScreenPlayers.IsBitSet( i ) || (g_bIterateRemoteSplitScreenPlayers && (g_RemoteSplitScreenPlayers[i] != NULL));
}
static int g_nCachedScreenSize[ 2 ] = { -1, -1 };
void VGui_OnScreenSizeChanged()
{
vgui::surface()->GetScreenSize( g_nCachedScreenSize[ 0 ], g_nCachedScreenSize[ 1 ] );
VGui_OnSplitScreenStateChanged();
}
static int g_nNumSplits = 1; //number of logical splits (local players + remote splits)
static int g_nNumLocalSplits = 1; //number of local players sitting at this computer
bool VGui_IsSplitScreen()
{
return g_nNumSplits >= 2;
}
bool VGui_IsSplitScreenPIP()
{
return VGui_IsSplitScreen() && g_nNumLocalSplits == ss_pipsplit.GetInt();
}
bool VGui_UsePipSplit()
{
return g_nNumLocalSplits <= ss_pipsplit.GetInt(); //ss_pipsplit 1 for remote splitscreen pip, ss_pipsplit 2 to use pip even with 2 local players
}
bool g_bSuppressConfigSystemLevelDueToPIPTransitions;
void VGui_OnSplitScreenStateChanged()
{
CUtlVector< Panel * > list;
VGui_GetPanelList( list );
g_SplitScreenPlayers.ClearAll();
g_nNumSplits = 0;
g_nNumLocalSplits = 0;
for ( int i = engine->FirstValidSplitScreenSlot();
i != -1;
i = engine->NextValidSplitScreenSlot( i ) )
{
g_SplitScreenPlayers.Set( i );
g_RemoteSplitScreenPlayers[i] = NULL; //actual splitscreen players nuke networked splitscreen players
++g_nNumSplits;
++g_nNumLocalSplits;
}
if( cl_enable_remote_splitscreen.GetBool() )
{
for( int i = 0; i != MAX_SPLITSCREEN_PLAYERS; ++i )
{
if( g_RemoteSplitScreenPlayers[i] != NULL )
{
++g_nNumSplits;
}
}
}
IterateRemoteSplitScreenViewSlots_Push( true );
g_LetterBox.SetNumSplitScreenPlayers( g_nNumSplits );
for ( int i = 0; i < MAX_SPLITSCREEN_PLAYERS; ++i )
{
list[ i ]->SetVisible( IsValidSplitScreenSlot( i ) );
}
// Now tile, etc. the rest of them
int sw, sh;
if ( g_nCachedScreenSize[ 0 ] == -1 )
{
vgui::surface()->GetScreenSize( g_nCachedScreenSize[ 0 ], g_nCachedScreenSize[ 1 ] );
}
sw = g_nCachedScreenSize[ 0 ];
sh = g_nCachedScreenSize[ 1 ];
VGUI_UpdateScreenSpaceBounds( g_nNumSplits, 0, 0, sw, sh );
// get the current splitscreen/letterbox settings. We only care about fov and viewmodelfov.
bool bDummy;
float flDummy, flFOV, flViewModelFOV;
g_LetterBox.GetSettings( &bDummy, &flDummy, &flFOV, &flViewModelFOV );
static SplitScreenConVarRef fov_desired( "fov_desired", true );
FOR_EACH_VALID_SPLITSCREEN_PLAYER( i )
{
if ( fov_desired.IsValid() )
{
fov_desired.SetValue( i, flFOV );
}
// The actual viewport panels are all at the top left of the screen, but sized appropriately
int x, y, w, h;
VGui_GetHudBounds( i, x, y, w, h);
list[ i ]->SetBounds( x, y, w, h );
surface()->SetAbsPosForContext( i, x, y );
}
// This is a hack to prevent changing the current system level during PIP mode transitions. Otherwise, on the next frame mat queue mode will be disabled for
// a frame and then re-enabled, which causes various known rendering problems and a noticeable hitch.
// I would have loved to plumb this down in a cleaner way, but this function is a convar change callback.
if ( !g_bSuppressConfigSystemLevelDueToPIPTransitions )
{
ConfigureCurrentSystemLevel( );
}
IterateRemoteSplitScreenViewSlots_Pop();
C_BaseEntity::UpdateVisibilityAllEntities();
}
void VGui_GetPanelBounds( int slot, int &x, int &y, int &w, int &h )
{
if ( !IsValidSplitScreenSlot( slot ) || g_nNumSplits == 1 )
{
x = y = 0;
vgui::surface()->GetScreenSize( w, h );
return;
}
vrect_t &r = g_ScreenSpaceBounds[ slot ];
x = r.x;
y = r.y;
w = r.width;
h = r.height;
}
void VGui_GetEngineRenderBounds( int slot, int &x, int &y, int &w, int &h, int &insetX, int &insetY )
{
insetX = insetY = 0;
if ( !IsValidSplitScreenSlot( slot ) || g_nNumSplits == 1 )
{
x = y = 0;
vgui::surface()->GetScreenSize( w, h );
return;
}
VGui_GetPanelBounds( slot, x, y, w, h );
bool bDummy = false;
float flDummy = 0;
float flAspect = 1.0f;
if ( !g_LetterBox.GetSettings( &bDummy, &flAspect, &flDummy, &flDummy ) )
{
return;
}
// Need to convert from physical to pixel aspect ratio. These aren't the same when using non-square pixels.
const AspectRatioInfo_t &aspectRatioInfo = materials->GetAspectRatioInfo();
flAspect *= aspectRatioInfo.m_flPhysicalToFrameBufferScalar;
// Figure out current aspect ratio
float flCurrentAspect = (float)w / (float)h;
float ratio = flAspect / flCurrentAspect;
if ( ratio > 1.0f )
{
// Screen is wider, need bars at top and bottom
int usetall = (float)w / flAspect;
if ( IsPC() )
{
insetY = ( h - usetall ) / 2;
y += insetY;
h = usetall;
}
else
{
// hopefully it centers, but it might not
usetall = AlignValue( usetall, 2 * GPU_RESOLVE_ALIGNMENT );
insetY = ( h - usetall ) / 2;
y += insetY;
y = AlignValue( y, GPU_RESOLVE_ALIGNMENT );
insetY = AlignValue( insetY, GPU_RESOLVE_ALIGNMENT );
h = usetall;
}
}
else
{
// Screen is narrower, need bars at left/right
int usewide = (float)h * flAspect;
if ( IsPC() )
{
insetX = ( w - usewide ) / 2;
x += insetX;
w = usewide;
}
else
{
// hopefully it centers, but it might not
usewide = AlignValue( usewide, 2 * GPU_RESOLVE_ALIGNMENT );
insetX = ( w - usewide ) / 2;
x += insetX;
x = AlignValue( x, GPU_RESOLVE_ALIGNMENT );
insetX = AlignValue( insetX, GPU_RESOLVE_ALIGNMENT );
w = usewide;
}
}
}
void VGui_GetHudBounds( int slot, int &x, int &y, int &w, int &h )
{
if ( !IsValidSplitScreenSlot( slot ) || g_nNumSplits == 1 )
{
x = y = 0;
vgui::surface()->GetScreenSize( w, h );
return;
}
bool bInset = false;
float dummy = 1.0f;
if ( !g_LetterBox.GetSettings( &bInset, &dummy, &dummy, &dummy ) ||
!bInset )
{
// Use entire bounds for HUD
VGui_GetPanelBounds( slot, x, y, w, h );
return;
}
int insetX = 0, insetY = 0;
VGui_GetEngineRenderBounds( slot, x, y, w, h, insetX, insetY );
}
int VGUI_FindSlotForRootPanel( vgui::Panel *pRoot )
{
CUtlVector< Panel * > list;
VGui_GetPanelList( list );
int slot = list.Find( pRoot ) ;
if ( slot == list.InvalidIndex() )
return 0;
return slot;
}