mirror of
https://github.com/nillerusr/source-engine.git
synced 2025-01-25 22:34:25 +00:00
1196 lines
32 KiB
C++
1196 lines
32 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose: Core implementation of vgui
|
|
//
|
|
// $NoKeywords: $
|
|
//===========================================================================//
|
|
|
|
|
|
#if defined( WIN32 ) && !defined( _X360 )
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#include "VGuiMatSurface/IMatSystemSurface.h"
|
|
#include <vgui/VGUI.h>
|
|
#include <vgui/Dar.h>
|
|
#include <vgui/IInputInternal.h>
|
|
#include <vgui/IPanel.h>
|
|
#include <vgui/ISystem.h>
|
|
#include <vgui/ISurface.h>
|
|
#include <vgui/IVGui.h>
|
|
#include <vgui/IClientPanel.h>
|
|
#include <vgui/IScheme.h>
|
|
#include <KeyValues.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <malloc.h>
|
|
#include <tier0/dbg.h>
|
|
#include <tier1/utlhandletable.h>
|
|
#include "vgui_internal.h"
|
|
#include "VPanel.h"
|
|
#include "IMessageListener.h"
|
|
#include "tier3/tier3.h"
|
|
#include "utllinkedlist.h"
|
|
#include "utlpriorityqueue.h"
|
|
#include "utlvector.h"
|
|
#include "tier0/vprof.h"
|
|
#include "tier0/icommandline.h"
|
|
|
|
#if defined( _X360 )
|
|
#include "xbox/xbox_win32stubs.h"
|
|
#endif
|
|
|
|
#undef GetCursorPos // protected_things.h defines this, and it makes it so we can't access g_pInput->GetCursorPos.
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include <tier0/memdbgon.h>
|
|
|
|
|
|
using namespace vgui;
|
|
static const int WARN_PANEL_NUMBER = 32768; // in DEBUG if more panels than this are created then throw an Assert, helps catch panel leaks
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Single item in the message queue
|
|
//-----------------------------------------------------------------------------
|
|
struct MessageItem_t
|
|
{
|
|
KeyValues *_params; // message data
|
|
// _params->GetName() is the message name
|
|
|
|
HPanel _messageTo; // the panel this message is to be sent to
|
|
HPanel _from; // the panel this message is from (if any)
|
|
float _arrivalTime; // time at which the message should be passed on to the recipient
|
|
|
|
int _messageID; // incrementing message index
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool PriorityQueueComp(const MessageItem_t& x, const MessageItem_t& y)
|
|
{
|
|
if (x._arrivalTime > y._arrivalTime)
|
|
{
|
|
return true;
|
|
}
|
|
else if (x._arrivalTime < y._arrivalTime)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// compare messageID's to ensure we have the messages in the correct order
|
|
return (x._messageID > y._messageID);
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Implementation of core vgui functionality
|
|
//-----------------------------------------------------------------------------
|
|
class CVGui : public CTier3AppSystem< IVGui >
|
|
{
|
|
typedef CTier3AppSystem< IVGui > BaseClass;
|
|
|
|
public:
|
|
CVGui();
|
|
~CVGui();
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// SRC specific stuff
|
|
// Here's where the app systems get to learn about each other
|
|
virtual bool Connect( CreateInterfaceFn factory );
|
|
virtual void Disconnect();
|
|
|
|
// Here's where systems can access other interfaces implemented by this object
|
|
// Returns NULL if it doesn't implement the requested interface
|
|
virtual void *QueryInterface( const char *pInterfaceName );
|
|
|
|
// Init, shutdown
|
|
virtual InitReturnVal_t Init();
|
|
virtual void Shutdown();
|
|
// End of specific interface
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
virtual void RunFrame();
|
|
|
|
virtual void Start()
|
|
{
|
|
m_bRunning = true;
|
|
}
|
|
|
|
// signals vgui to Stop running
|
|
virtual void Stop()
|
|
{
|
|
m_bRunning = false;
|
|
}
|
|
|
|
// returns true if vgui is current active
|
|
virtual bool IsRunning()
|
|
{
|
|
return m_bRunning;
|
|
}
|
|
|
|
virtual void ShutdownMessage(unsigned int shutdownID);
|
|
|
|
// safe-pointer handle methods
|
|
virtual VPANEL AllocPanel();
|
|
virtual void FreePanel(VPANEL ipanel);
|
|
virtual HPanel PanelToHandle(VPANEL panel);
|
|
virtual VPANEL HandleToPanel(HPanel index);
|
|
virtual void MarkPanelForDeletion(VPANEL panel);
|
|
|
|
virtual void AddTickSignal(VPANEL panel, int intervalMilliseconds = 0);
|
|
virtual void AddTickSignalToHead( VPANEL panel, int intervalMilliseconds = 0 ) OVERRIDE;
|
|
virtual void RemoveTickSignal(VPANEL panel );
|
|
|
|
|
|
// message pump method
|
|
virtual void PostMessage(VPANEL target, KeyValues *params, VPANEL from, float delaySeconds = 0.0f);
|
|
|
|
virtual void SetSleep( bool state ) { m_bDoSleep = state; };
|
|
virtual bool GetShouldVGuiControlSleep() { return m_bDoSleep; }
|
|
|
|
virtual void DPrintf(const char *format, ...);
|
|
virtual void DPrintf2(const char *format, ...);
|
|
virtual void SpewAllActivePanelNames();
|
|
|
|
// Creates/ destroys vgui contexts, which contains information
|
|
// about which controls have mouse + key focus, for example.
|
|
virtual HContext CreateContext();
|
|
virtual void DestroyContext( HContext context );
|
|
|
|
// Associates a particular panel with a vgui context
|
|
// Associating NULL is valid; it disconnects the panel from the context
|
|
virtual void AssociatePanelWithContext( HContext context, VPANEL pRoot );
|
|
|
|
// Activates a particular input context, use DEFAULT_VGUI_CONTEXT
|
|
// to get the one normally used by VGUI
|
|
virtual void ActivateContext( HContext context );
|
|
|
|
// enables VR mode
|
|
virtual void SetVRMode( bool bVRMode ) OVERRIDE
|
|
{
|
|
m_bVRMode = bVRMode;
|
|
}
|
|
virtual bool GetVRMode() OVERRIDE
|
|
{
|
|
return m_bVRMode;
|
|
}
|
|
|
|
bool IsDispatchingMessages( void )
|
|
{
|
|
return m_InDispatcher;
|
|
}
|
|
|
|
private:
|
|
// VGUI contexts
|
|
struct Context_t
|
|
{
|
|
HInputContext m_hInputContext;
|
|
};
|
|
|
|
struct Tick_t
|
|
{
|
|
VPanel *panel;
|
|
int interval;
|
|
int nexttick;
|
|
bool bMarkDeleted;
|
|
// Debugging
|
|
char panelname[ 64 ];
|
|
};
|
|
|
|
Tick_t* CreateNewTick( VPANEL panel, int intervalMilliseconds );
|
|
|
|
// Returns the current context
|
|
Context_t *GetContext( HContext context );
|
|
|
|
void PanelCreated(VPanel *panel);
|
|
void PanelDeleted(VPanel *panel);
|
|
bool DispatchMessages();
|
|
void DestroyAllContexts( );
|
|
void ClearMessageQueues();
|
|
inline bool IsReentrant() const
|
|
{
|
|
return m_nReentrancyCount > 0;
|
|
}
|
|
|
|
// safe panel handle stuff
|
|
CUtlHandleTable< VPanel, 20 > m_HandleTable;
|
|
int m_iCurrentMessageID;
|
|
|
|
bool m_bRunning : 1;
|
|
bool m_bDoSleep : 1;
|
|
bool m_InDispatcher : 1;
|
|
bool m_bDebugMessages : 1;
|
|
bool m_bVRMode : 1;
|
|
bool m_bCanRemoveTickSignal : 1;
|
|
int m_nReentrancyCount;
|
|
|
|
CUtlVector< Tick_t * > m_TickSignalVec;
|
|
CUtlLinkedList< Context_t > m_Contexts;
|
|
|
|
HContext m_hContext;
|
|
Context_t m_DefaultContext;
|
|
|
|
#ifdef DEBUG
|
|
int m_iDeleteCount, m_iDeletePanelCount;
|
|
#endif
|
|
|
|
// message queue. holds all vgui messages generated by windows events
|
|
CUtlLinkedList<MessageItem_t, ushort> m_MessageQueue;
|
|
|
|
// secondary message queue, holds all vgui messages generated by vgui
|
|
CUtlLinkedList<MessageItem_t, ushort> m_SecondaryQueue;
|
|
|
|
// timing queue, holds all the messages that have to arrive at a specified time
|
|
CUtlPriorityQueue<MessageItem_t> m_DelayedMessageQueue;
|
|
};
|
|
|
|
CVGui g_VGui;
|
|
EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CVGui, IVGui, VGUI_IVGUI_INTERFACE_VERSION, g_VGui);
|
|
|
|
bool IsDispatchingMessageQueue( void )
|
|
{
|
|
return g_VGui.IsDispatchingMessages();
|
|
}
|
|
|
|
namespace vgui
|
|
{
|
|
IVGui *g_pIVgui = &g_VGui;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Constructor
|
|
//-----------------------------------------------------------------------------
|
|
CVGui::CVGui() : m_DelayedMessageQueue(0, 4, PriorityQueueComp)
|
|
{
|
|
m_bRunning = false;
|
|
m_InDispatcher = false;
|
|
m_bDebugMessages = false;
|
|
m_bDoSleep = true;
|
|
m_bVRMode = false;
|
|
m_bCanRemoveTickSignal = true;
|
|
m_nReentrancyCount = 0;
|
|
m_hContext = DEFAULT_VGUI_CONTEXT;
|
|
m_DefaultContext.m_hInputContext = DEFAULT_INPUT_CONTEXT;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Destructor
|
|
//-----------------------------------------------------------------------------
|
|
CVGui::~CVGui()
|
|
{
|
|
#ifdef _DEBUG
|
|
int nCount = m_HandleTable.GetHandleCount();
|
|
int nActualCount = 0;
|
|
for ( int i = 0; i < nCount; ++i )
|
|
{
|
|
UtlHandle_t h = m_HandleTable.GetHandleFromIndex( i );
|
|
if ( m_HandleTable.IsHandleValid( h ) )
|
|
{
|
|
++nActualCount;
|
|
}
|
|
}
|
|
|
|
if ( nActualCount > 0 )
|
|
{
|
|
Msg("Memory leak: panels left in CVGui::m_PanelList: %d\n", nActualCount );
|
|
}
|
|
#endif // _DEBUG
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Dumps out list of all active panels
|
|
//-----------------------------------------------------------------------------
|
|
void CVGui::SpewAllActivePanelNames()
|
|
{
|
|
int nCount = m_HandleTable.GetHandleCount();
|
|
for ( int i = 0; i < nCount; ++i )
|
|
{
|
|
UtlHandle_t h = m_HandleTable.GetHandleFromIndex( i );
|
|
if ( m_HandleTable.IsHandleValid( h ) )
|
|
{
|
|
VPanel *pPanel = m_HandleTable.GetHandle( h );
|
|
Msg("\tpanel '%s' of type '%s' leaked\n", g_pIPanel->GetName( (VPANEL)pPanel ), ((VPanel *)pPanel)->GetClassName());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Creates/ destroys "input" contexts, which contains information
|
|
// about which controls have mouse + key focus, for example.
|
|
//-----------------------------------------------------------------------------
|
|
HContext CVGui::CreateContext()
|
|
{
|
|
HContext i = m_Contexts.AddToTail();
|
|
m_Contexts[i].m_hInputContext = g_pInput->CreateInputContext();
|
|
return i;
|
|
}
|
|
|
|
void CVGui::DestroyContext( HContext context )
|
|
{
|
|
Assert( context != DEFAULT_VGUI_CONTEXT );
|
|
|
|
if ( m_hContext == context )
|
|
{
|
|
ActivateContext( DEFAULT_VGUI_CONTEXT );
|
|
}
|
|
|
|
g_pInput->DestroyInputContext( GetContext(context)->m_hInputContext );
|
|
m_Contexts.Remove(context);
|
|
}
|
|
|
|
void CVGui::DestroyAllContexts( )
|
|
{
|
|
HContext next;
|
|
HContext i = m_Contexts.Head();
|
|
while (i != m_Contexts.InvalidIndex())
|
|
{
|
|
next = m_Contexts.Next(i);
|
|
DestroyContext( i );
|
|
i = next;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the current context
|
|
//-----------------------------------------------------------------------------
|
|
CVGui::Context_t *CVGui::GetContext( HContext context )
|
|
{
|
|
if (context == DEFAULT_VGUI_CONTEXT)
|
|
return &m_DefaultContext;
|
|
return &m_Contexts[context];
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Associates a particular panel with a context
|
|
// Associating NULL is valid; it disconnects the panel from the context
|
|
//-----------------------------------------------------------------------------
|
|
void CVGui::AssociatePanelWithContext( HContext context, VPANEL pRoot )
|
|
{
|
|
Assert( context != DEFAULT_VGUI_CONTEXT );
|
|
g_pInput->AssociatePanelWithInputContext( GetContext(context)->m_hInputContext, pRoot );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Activates a particular context, use DEFAULT_VGUI_CONTEXT
|
|
// to get the one normally used by VGUI
|
|
//-----------------------------------------------------------------------------
|
|
void CVGui::ActivateContext( HContext context )
|
|
{
|
|
Assert( (context == DEFAULT_VGUI_CONTEXT) || m_Contexts.IsValidIndex(context) );
|
|
|
|
if ( m_hContext != context )
|
|
{
|
|
// Clear out any messages queues that may be full...
|
|
if ( !IsReentrant() )
|
|
{
|
|
DispatchMessages();
|
|
}
|
|
|
|
m_hContext = context;
|
|
g_pInput->ActivateInputContext( GetContext(m_hContext)->m_hInputContext );
|
|
|
|
if ( context != DEFAULT_VGUI_CONTEXT && !IsReentrant() )
|
|
{
|
|
g_pInput->RunFrame( );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Runs a single vgui frame, pumping all message to panels
|
|
//-----------------------------------------------------------------------------
|
|
void CVGui::RunFrame()
|
|
{
|
|
// NOTE: This can happen when running in Maya waiting for modal dialogs
|
|
bool bIsReentrant = m_InDispatcher;
|
|
if ( bIsReentrant )
|
|
{
|
|
++m_nReentrancyCount;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
// memory allocation debug helper
|
|
// DPrintf( "Delete Count:%i,%i\n", m_iDeleteCount, m_iDeletePanelCount );
|
|
// m_iDeleteCount = m_iDeletePanelCount = 0;
|
|
#endif
|
|
|
|
// this will generate all key and mouse events as well as make a real repaint
|
|
{
|
|
VPROF( "surface()->RunFrame()" );
|
|
g_pSurface->RunFrame();
|
|
}
|
|
|
|
// give the system a chance to process
|
|
{
|
|
VPROF( "system()->RunFrame()" );
|
|
g_pSystem->RunFrame();
|
|
}
|
|
|
|
// update cursor positions
|
|
if ( IsPC() && !IsReentrant() )
|
|
{
|
|
VPROF( "update cursor positions" );
|
|
int cursorX, cursorY;
|
|
g_pInput->GetCursorPosition(cursorX, cursorY);
|
|
|
|
// this does the actual work given a x,y and a surface
|
|
g_pInput->UpdateMouseFocus(cursorX, cursorY);
|
|
|
|
}
|
|
|
|
if ( !bIsReentrant )
|
|
{
|
|
VPROF( "input()->RunFrame()" );
|
|
g_pInput->RunFrame();
|
|
}
|
|
|
|
// messenging
|
|
if ( !bIsReentrant )
|
|
{
|
|
VPROF( "messaging" );
|
|
|
|
// send all the messages waiting in the queue
|
|
DispatchMessages();
|
|
|
|
// Do the OnTicks before purging messages, since in previous code they were posted after dispatch and wouldn't hit
|
|
// until next frame
|
|
int time = g_pSystem->GetTimeMillis();
|
|
|
|
m_bCanRemoveTickSignal = false;
|
|
|
|
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Ticks", __FUNCTION__ );
|
|
// directly invoke tick all who asked to be ticked
|
|
int count = m_TickSignalVec.Count();
|
|
for (int i = count - 1; i >= 0; i-- )
|
|
{
|
|
Tick_t *t = m_TickSignalVec[i];
|
|
if ( t->bMarkDeleted )
|
|
continue;
|
|
|
|
if ( t->interval != 0 )
|
|
{
|
|
if ( time < t->nexttick )
|
|
continue;
|
|
|
|
t->nexttick = time + t->interval;
|
|
}
|
|
|
|
t->panel->Client()->OnTick();
|
|
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Ticks: %s", __FUNCTION__, t->panel->Client()->GetName() );
|
|
}
|
|
|
|
m_bCanRemoveTickSignal = true;
|
|
|
|
// get count again. panels could be added to tick vector in OnTick
|
|
count = m_TickSignalVec.Count();
|
|
|
|
// Remove all panels that tried to remove tick in OnTick
|
|
for (int i = count - 1; i >= 0; i-- )
|
|
{
|
|
Tick_t *t = m_TickSignalVec[i];
|
|
if ( t->bMarkDeleted )
|
|
{
|
|
m_TickSignalVec.Remove( i );
|
|
delete t;
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
VPROF( "SolveTraverse" );
|
|
// make sure the hierarchy is up to date
|
|
g_pSurface->SolveTraverse(g_pSurface->GetEmbeddedPanel());
|
|
g_pSurface->ApplyChanges();
|
|
#ifdef WIN32
|
|
Assert( IsX360() || ( IsPC() && _heapchk() == _HEAPOK ) );
|
|
#endif
|
|
}
|
|
|
|
if ( bIsReentrant )
|
|
{
|
|
--m_nReentrancyCount;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
VPANEL CVGui::AllocPanel()
|
|
{
|
|
#ifdef DEBUG
|
|
m_iDeleteCount++;
|
|
#endif
|
|
|
|
VPanel *panel = new VPanel;
|
|
PanelCreated(panel);
|
|
return (VPANEL)panel;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CVGui::FreePanel(VPANEL ipanel)
|
|
{
|
|
PanelDeleted((VPanel *)ipanel);
|
|
delete (VPanel *)ipanel;
|
|
#ifdef DEBUG
|
|
m_iDeleteCount--;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns the safe index of the panel
|
|
//-----------------------------------------------------------------------------
|
|
HPanel CVGui::PanelToHandle(VPANEL panel)
|
|
{
|
|
if (panel)
|
|
return ((VPanel*)panel)->GetHPanel();
|
|
return INVALID_PANEL;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns the panel at the specified index
|
|
//-----------------------------------------------------------------------------
|
|
VPANEL CVGui::HandleToPanel(HPanel index)
|
|
{
|
|
if ( !m_HandleTable.IsHandleValid( index ) )
|
|
{
|
|
return NULL;
|
|
}
|
|
return (VPANEL)m_HandleTable.GetHandle( (UtlHandle_t)index );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Called whenever a panel is constructed
|
|
//-----------------------------------------------------------------------------
|
|
void CVGui::PanelCreated(VPanel *panel)
|
|
{
|
|
UtlHandle_t h = m_HandleTable.AddHandle();
|
|
m_HandleTable.SetHandle( h, panel );
|
|
|
|
#if DUMP_PANEL_LIST
|
|
int nCount = m_HandleTable.GetHandleCount();
|
|
int nActualCount = 0;
|
|
for ( int i = 0; i < nCount; ++i )
|
|
{
|
|
UtlHandle_t h = m_HandleTable.GetHandleFromIndex( i );
|
|
if ( m_HandleTable.IsHandleValid( h ) )
|
|
{
|
|
++nActualCount;
|
|
}
|
|
}
|
|
|
|
if ( nActualCount >= WARN_PANEL_NUMBER )
|
|
{
|
|
FILE *file1 = fopen("panellist.txt", "w");
|
|
if (file1 != NULL)
|
|
{
|
|
fprintf(file1, "Too many panels...listing them all.\n");
|
|
int panelIndex;
|
|
for (panelIndex = 0; panelIndex < nCount; ++panelIndex)
|
|
{
|
|
UtlHandle_t h = m_HandleTable.GetHandleFromIndex( i );
|
|
VPanel *pPanel = m_HandleTable.GetHandle( h );
|
|
IClientPanel *ipanel = ( pPanel ) ? pPanel->Client() : NULL;
|
|
if ( ipanel )
|
|
fprintf(file1, "panel %d: name: %s classname: %s\n", panelIndex, ipanel->GetName(), ipanel->GetClassName());
|
|
else
|
|
fprintf(file1, "panel %d: can't get ipanel\n", panelIndex);
|
|
}
|
|
|
|
fclose(file1);
|
|
}
|
|
}
|
|
|
|
Assert( nActualCount < WARN_PANEL_NUMBER );
|
|
#endif // DUMP_PANEL_LIST
|
|
|
|
((VPanel *)panel)->SetHPanel( h );
|
|
|
|
g_pSurface->AddPanel((VPANEL)panel);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: instantly stops the app from pointing to the focus'd object
|
|
// used when an object is being deleted
|
|
//-----------------------------------------------------------------------------
|
|
void CVGui::PanelDeleted(VPanel *focus)
|
|
{
|
|
Assert( focus );
|
|
g_pSurface->ReleasePanel((VPANEL)focus);
|
|
g_pInput->PanelDeleted((VPANEL)focus);
|
|
|
|
// remove from safe handle list
|
|
UtlHandle_t h = ((VPanel *)focus)->GetHPanel();
|
|
|
|
Assert( m_HandleTable.IsHandleValid(h) );
|
|
if ( m_HandleTable.IsHandleValid(h) )
|
|
{
|
|
m_HandleTable.RemoveHandle( h );
|
|
}
|
|
|
|
((VPanel *)focus)->SetHPanel( INVALID_PANEL );
|
|
|
|
// remove from tick signal dar
|
|
RemoveTickSignal( (VPANEL)focus );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Creates or updates a tick signal for a panel. Returns NULL if already ticking.
|
|
//-----------------------------------------------------------------------------
|
|
CVGui::Tick_t* CVGui::CreateNewTick( VPANEL panel, int intervalMilliseconds )
|
|
{
|
|
Tick_t *t;
|
|
// See if it's already in list
|
|
int count = m_TickSignalVec.Count();
|
|
for (int i = 0; i < count; i++ )
|
|
{
|
|
Tick_t *t = m_TickSignalVec[i];
|
|
if ( t->panel == (VPanel *)panel )
|
|
{
|
|
// Go ahead and update intervals
|
|
t->interval = intervalMilliseconds;
|
|
t->nexttick = g_pSystem->GetTimeMillis() + t->interval;
|
|
|
|
// Somebody added this panel back to the tick list, don't delete it
|
|
t->bMarkDeleted = false;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// Add to list
|
|
t = new Tick_t;
|
|
|
|
t->panel = (VPanel *)panel;
|
|
t->interval = intervalMilliseconds;
|
|
t->nexttick = g_pSystem->GetTimeMillis() + t->interval;
|
|
t->bMarkDeleted = false;
|
|
|
|
if ( strlen( ((VPanel *)panel)->Client()->GetName() ) > 0 )
|
|
{
|
|
strncpy( t->panelname, ((VPanel *)panel)->Client()->GetName(), sizeof( t->panelname ) );
|
|
}
|
|
else
|
|
{
|
|
strncpy( t->panelname, ((VPanel *)panel)->Client()->GetClassName(), sizeof( t->panelname ) );
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Adds the panel to the tail of a tick signal list, so the panel receives a message every frame
|
|
//-----------------------------------------------------------------------------
|
|
void CVGui::AddTickSignal(VPANEL panel, int intervalMilliseconds /*=0*/ )
|
|
{
|
|
Tick_t* t = CreateNewTick( panel, intervalMilliseconds );
|
|
|
|
if ( t )
|
|
{
|
|
// add the element to the end list
|
|
m_TickSignalVec.AddToTail( t );
|
|
// panel is removed from list when deleted
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Adds the panel to the head of a tick signal list, so the panel receives a message every frame
|
|
//-----------------------------------------------------------------------------
|
|
void CVGui::AddTickSignalToHead(VPANEL panel, int intervalMilliseconds /*=0*/ )
|
|
{
|
|
Tick_t* t = CreateNewTick( panel, intervalMilliseconds );
|
|
|
|
if ( t )
|
|
{
|
|
// simply add the element to the head list
|
|
m_TickSignalVec.AddToHead( t );
|
|
// panel is removed from list when deleted
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CVGui::RemoveTickSignal( VPANEL panel )
|
|
{
|
|
VPanel *search = (VPanel *)panel;
|
|
|
|
// remove from tick signal dar
|
|
int count = m_TickSignalVec.Count();
|
|
|
|
for (int i = 0; i < count; i++ )
|
|
{
|
|
Tick_t *tick = m_TickSignalVec[i];
|
|
if ( tick->panel == search )
|
|
{
|
|
if ( m_bCanRemoveTickSignal )
|
|
{
|
|
m_TickSignalVec.Remove( i );
|
|
delete tick;
|
|
}
|
|
else
|
|
{
|
|
tick->bMarkDeleted = true;
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: message pump
|
|
// loops through and sends all active messages
|
|
// note that more messages may be posted during the process
|
|
//-----------------------------------------------------------------------------
|
|
bool CVGui::DispatchMessages()
|
|
{
|
|
int time = g_pSystem->GetTimeMillis();
|
|
|
|
m_InDispatcher = true;
|
|
bool doneWork = (m_MessageQueue.Count() > 12);
|
|
|
|
bool bUsingDelayedQueue = (m_DelayedMessageQueue.Count() > 0);
|
|
|
|
// Need two passes because we send the mouse move message after all
|
|
// other messages are done, but the mouse move message may itself generate
|
|
// some more messages
|
|
int nPassCount = 0;
|
|
while ( nPassCount < 2 )
|
|
{
|
|
while (m_MessageQueue.Count() > 0 || (m_SecondaryQueue.Count() > 0) || bUsingDelayedQueue)
|
|
{
|
|
// get the first message
|
|
MessageItem_t *messageItem = NULL;
|
|
int messageIndex = 0;
|
|
|
|
// use the secondary queue until it empties. empty it after each message in the
|
|
// primary queue. this makes primary messages completely resolve
|
|
bool bUsingSecondaryQueue = (m_SecondaryQueue.Count() > 0);
|
|
if (bUsingSecondaryQueue)
|
|
{
|
|
doneWork = true;
|
|
messageIndex = m_SecondaryQueue.Head();
|
|
messageItem = &m_SecondaryQueue[messageIndex];
|
|
}
|
|
else if (bUsingDelayedQueue)
|
|
{
|
|
if (m_DelayedMessageQueue.Count() >0)
|
|
{
|
|
messageItem = (MessageItem_t*)&m_DelayedMessageQueue.ElementAtHead();
|
|
}
|
|
if (!messageItem || messageItem->_arrivalTime > time)
|
|
{
|
|
// no more items in the delayed message queue, move to the system queue
|
|
bUsingDelayedQueue = false;
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
messageIndex = m_MessageQueue.Head();
|
|
messageItem = &m_MessageQueue[messageIndex];
|
|
}
|
|
|
|
// message debug code
|
|
|
|
if ( m_bDebugMessages )
|
|
{
|
|
const char *qname = bUsingSecondaryQueue ? "Secondary" : "Primary";
|
|
|
|
if (strcmp(messageItem->_params->GetName(), "Tick")
|
|
&& strcmp(messageItem->_params->GetName(), "MouseFocusTicked")
|
|
&& strcmp(messageItem->_params->GetName(), "KeyFocusTicked")
|
|
&& strcmp(messageItem->_params->GetName(), "CursorMoved"))
|
|
{
|
|
if (!stricmp(messageItem->_params->GetName(), "command"))
|
|
{
|
|
g_pIVgui->DPrintf2( "%s Queue dispatching command( %s, %s -- %i )\n", qname, messageItem->_params->GetName(), messageItem->_params->GetString("command"), messageItem->_messageID );
|
|
}
|
|
else
|
|
{
|
|
g_pIVgui->DPrintf2( "%s Queue dispatching( %s -- %i )\n", qname ,messageItem->_params->GetName(), messageItem->_messageID );
|
|
}
|
|
}
|
|
}
|
|
|
|
// send it
|
|
KeyValues *params = messageItem->_params;
|
|
|
|
// Deal with special internal cursor movement messages
|
|
if ( messageItem->_messageTo == 0xFFFFFFFF )
|
|
{
|
|
if ( !Q_stricmp( params->GetName(), "SetCursorPosInternal" ) )
|
|
{
|
|
int nXPos = params->GetInt( "xpos", 0 );
|
|
int nYPos = params->GetInt( "ypos", 0 );
|
|
g_pInput->UpdateCursorPosInternal( nXPos, nYPos );
|
|
}
|
|
}
|
|
#ifdef _X360
|
|
else if ( messageItem->_messageTo == 0xFFFFFFFE ) // special tag to always give message to the active key focus
|
|
{
|
|
VPanel *vto = (VPanel *) g_pInput->GetCalculatedFocus();
|
|
if (vto)
|
|
{
|
|
vto->SendMessage(params, g_pIVgui->HandleToPanel(messageItem->_from));
|
|
}
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
VPanel *vto = (VPanel *)g_pIVgui->HandleToPanel(messageItem->_messageTo);
|
|
if (vto)
|
|
{
|
|
// Msg("Sending message: %s to %s\n", params ? params->GetName() : "\"\"", vto->GetName() ? vto->GetName() : "\"\"");
|
|
vto->SendMessage(params, g_pIVgui->HandleToPanel(messageItem->_from));
|
|
}
|
|
}
|
|
|
|
// free the keyvalues memory
|
|
// we can't reference the messageItem pointer anymore since the queue might have moved in memory
|
|
if (params)
|
|
{
|
|
params->deleteThis();
|
|
}
|
|
|
|
// remove it from the queue
|
|
if (bUsingSecondaryQueue)
|
|
{
|
|
m_SecondaryQueue.Remove(messageIndex);
|
|
}
|
|
else if (bUsingDelayedQueue)
|
|
{
|
|
m_DelayedMessageQueue.RemoveAtHead();
|
|
}
|
|
else
|
|
{
|
|
m_MessageQueue.Remove(messageIndex);
|
|
}
|
|
}
|
|
|
|
++nPassCount;
|
|
if ( nPassCount == 1 )
|
|
{
|
|
// Specifically post the current mouse position as a message
|
|
g_pInput->PostCursorMessage();
|
|
}
|
|
}
|
|
|
|
// Make sure the windows cursor is in the right place after processing input
|
|
// Needs to be done here because a message provoked by the cursor moved
|
|
// message may move the cursor also
|
|
g_pInput->HandleExplicitSetCursor( );
|
|
|
|
m_InDispatcher = false;
|
|
return doneWork;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CVGui::MarkPanelForDeletion(VPANEL panel)
|
|
{
|
|
PostMessage(panel, new KeyValues("Delete"), NULL);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Adds a message to the queue to be sent to a user
|
|
//-----------------------------------------------------------------------------
|
|
void CVGui::PostMessage(VPANEL target, KeyValues *params, VPANEL from, float delay)
|
|
{
|
|
// Ignore all messages in re-entrant mode
|
|
if ( IsReentrant() )
|
|
{
|
|
Assert( 0 );
|
|
if (params)
|
|
{
|
|
params->deleteThis();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!target)
|
|
{
|
|
if (params)
|
|
{
|
|
params->deleteThis();
|
|
}
|
|
return;
|
|
}
|
|
|
|
MessageItem_t messageItem;
|
|
|
|
#ifdef _X360
|
|
// Special coded target that will always send the message to the key focus
|
|
// this is needed since we might send two messages on a tice, and the first
|
|
// could change the focus.
|
|
if( target == (VPANEL) MESSAGE_CURRENT_KEYFOCUS )
|
|
{
|
|
messageItem._messageTo = 0xFFFFFFFE;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
messageItem._messageTo = (target != (VPANEL) MESSAGE_CURSOR_POS ) ? g_pIVgui->PanelToHandle(target) : 0xFFFFFFFF;
|
|
}
|
|
messageItem._params = params;
|
|
Assert(params->GetName());
|
|
messageItem._from = g_pIVgui->PanelToHandle(from);
|
|
messageItem._arrivalTime = 0;
|
|
messageItem._messageID = m_iCurrentMessageID++;
|
|
|
|
/* message debug code
|
|
//if ( stricmp(messageItem._params->GetName(),"CursorMoved") && stricmp(messageItem._params->GetName(),"KeyFocusTicked"))
|
|
{
|
|
g_pIVgui->DPrintf2( "posting( %s -- %i )\n", messageItem._params->GetName(), messageItem._messageID );
|
|
}
|
|
*/
|
|
|
|
// add the message to the correct message queue
|
|
if (delay > 0.0f)
|
|
{
|
|
messageItem._arrivalTime = g_pSystem->GetTimeMillis() + (delay * 1000);
|
|
m_DelayedMessageQueue.Insert(messageItem);
|
|
}
|
|
else if (m_InDispatcher)
|
|
{
|
|
m_SecondaryQueue.AddToTail(messageItem);
|
|
}
|
|
else
|
|
{
|
|
m_MessageQueue.AddToTail(messageItem);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CVGui::ShutdownMessage(unsigned int shutdownID)
|
|
{
|
|
// broadcast Shutdown to all the top level windows, and see if any take notice
|
|
VPANEL panel = g_pSurface->GetEmbeddedPanel();
|
|
for (int i = 0; i < ((VPanel *)panel)->GetChildCount(); i++)
|
|
{
|
|
g_pIVgui->PostMessage((VPANEL)((VPanel *)panel)->GetChild(i), new KeyValues("ShutdownRequest", "id", shutdownID), NULL);
|
|
}
|
|
|
|
// post to the top level window as well
|
|
g_pIVgui->PostMessage(panel, new KeyValues("ShutdownRequest", "id", shutdownID), NULL);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Clears all the memory queues and free's their memory
|
|
//-----------------------------------------------------------------------------
|
|
void CVGui::ClearMessageQueues()
|
|
{
|
|
Assert(!m_InDispatcher);
|
|
|
|
{FOR_EACH_LL( m_MessageQueue, i )
|
|
{
|
|
if (m_MessageQueue[i]._params)
|
|
{
|
|
m_MessageQueue[i]._params->deleteThis();
|
|
}
|
|
}}
|
|
m_MessageQueue.RemoveAll();
|
|
|
|
// secondary message queue, holds all vgui messages generated by vgui
|
|
{FOR_EACH_LL( m_SecondaryQueue, i )
|
|
{
|
|
if (m_SecondaryQueue[i]._params)
|
|
{
|
|
m_SecondaryQueue[i]._params->deleteThis();
|
|
}
|
|
}}
|
|
m_SecondaryQueue.RemoveAll();
|
|
|
|
// timing queue, holds all the messages that have to arrive at a specified time
|
|
while (m_DelayedMessageQueue.Count() > 0)
|
|
{
|
|
if (m_DelayedMessageQueue.ElementAtHead()._params)
|
|
{
|
|
m_DelayedMessageQueue.ElementAtHead()._params->deleteThis();
|
|
}
|
|
m_DelayedMessageQueue.RemoveAtHead();
|
|
}
|
|
}
|
|
|
|
/*
|
|
static void*(*staticMalloc)(size_t size)=malloc;
|
|
static void(*staticFree)(void* memblock)=free;
|
|
|
|
static int g_iMemoryBlocksAllocated = 0;
|
|
|
|
void *operator new(size_t size)
|
|
{
|
|
g_iMemoryBlocksAllocated += 1;
|
|
return staticMalloc(size);
|
|
}
|
|
|
|
void operator delete(void* memblock)
|
|
{
|
|
if (!memblock)
|
|
return;
|
|
|
|
g_iMemoryBlocksAllocated -= 1;
|
|
|
|
if (g_iMemoryBlocksAllocated < 0)
|
|
{
|
|
int x = 3;
|
|
}
|
|
|
|
staticFree(memblock);
|
|
}
|
|
|
|
void *operator new [] (size_t size)
|
|
{
|
|
return staticMalloc(size);
|
|
}
|
|
|
|
void operator delete [] (void *pMem)
|
|
{
|
|
staticFree(pMem);
|
|
}
|
|
*/
|
|
|
|
void CVGui::DPrintf(const char* format,...)
|
|
{
|
|
char buf[2048];
|
|
va_list argList;
|
|
|
|
va_start(argList,format);
|
|
Q_vsnprintf(buf,sizeof( buf ), format,argList);
|
|
va_end(argList);
|
|
|
|
#ifdef WIN32
|
|
::OutputDebugString(buf);
|
|
#else
|
|
Msg( "%s", buf );
|
|
#endif
|
|
}
|
|
|
|
void CVGui::DPrintf2(const char* format,...)
|
|
{
|
|
char buf[2048];
|
|
va_list argList;
|
|
static int ctr=0;
|
|
|
|
Q_snprintf(buf,sizeof( buf ), "%d:",ctr++ );
|
|
|
|
va_start(argList,format);
|
|
Q_vsnprintf(buf+strlen(buf),sizeof( buf )-strlen(buf),format,argList);
|
|
va_end(argList);
|
|
|
|
#ifdef WIN32
|
|
::OutputDebugString(buf);
|
|
#else
|
|
Msg( "%s", buf );
|
|
#endif
|
|
}
|
|
|
|
void vgui::vgui_strcpy(char* dst,int dstLen,const char* src)
|
|
{
|
|
Assert(dst!=null);
|
|
Assert(dstLen>=0);
|
|
Assert(src!=null);
|
|
|
|
int srcLen=strlen(src)+1;
|
|
if(srcLen>dstLen)
|
|
{
|
|
srcLen=dstLen;
|
|
}
|
|
|
|
memcpy(dst,src,srcLen-1);
|
|
dst[srcLen-1]=0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// HL2/TFC specific stuff
|
|
//-----------------------------------------------------------------------------
|
|
// Here's where the app systems get to learn about each other
|
|
//-----------------------------------------------------------------------------
|
|
bool CVGui::Connect( CreateInterfaceFn factory )
|
|
{
|
|
if ( !BaseClass::Connect( factory ) )
|
|
return false;
|
|
|
|
if ( !g_pFullFileSystem || !g_pVGuiLocalize )
|
|
{
|
|
Warning( "IVGui unable to connect to required interfaces!\n" );
|
|
return false;
|
|
}
|
|
|
|
return VGui_InternalLoadInterfaces( &factory, 1 );
|
|
}
|
|
|
|
void CVGui::Disconnect()
|
|
{
|
|
// FIXME: Blat out interface pointers
|
|
BaseClass::Disconnect();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Init, shutdown
|
|
//-----------------------------------------------------------------------------
|
|
InitReturnVal_t CVGui::Init()
|
|
{
|
|
m_hContext = DEFAULT_VGUI_CONTEXT;
|
|
m_bDebugMessages = CommandLine()->FindParm( "-vguimessages" ) ? true : false;
|
|
|
|
InitReturnVal_t nRetVal = BaseClass::Init();
|
|
if ( nRetVal != INIT_OK )
|
|
return nRetVal;
|
|
|
|
return INIT_OK;
|
|
}
|
|
|
|
void CVGui::Shutdown()
|
|
{
|
|
g_pSystem->SaveUserConfigFile();
|
|
|
|
DestroyAllContexts();
|
|
ClearMessageQueues();
|
|
|
|
g_pSystem->Shutdown();
|
|
g_pScheme->Shutdown(true);
|
|
|
|
if ( !g_pSurface->QueryInterface( MAT_SYSTEM_SURFACE_INTERFACE_VERSION ) )
|
|
{
|
|
g_pSurface->Shutdown();
|
|
}
|
|
|
|
BaseClass::Shutdown();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Here's where systems can access other interfaces implemented by this object
|
|
// Returns NULL if it doesn't implement the requested interface
|
|
//-----------------------------------------------------------------------------
|
|
void *CVGui::QueryInterface( const char *pInterfaceName )
|
|
{
|
|
// FIXME: Should this go here?
|
|
// Access other global interfaces exposed by this system...
|
|
CreateInterfaceFn vguiFactory = Sys_GetFactoryThis();
|
|
return vguiFactory( pInterfaceName, NULL );
|
|
}
|