You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1195 lines
32 KiB
1195 lines
32 KiB
//========= 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 ); |
|
}
|
|
|