diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /vgui2/src/vgui.cpp | |
| download | archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip | |
Diffstat (limited to 'vgui2/src/vgui.cpp')
| -rw-r--r-- | vgui2/src/vgui.cpp | 1195 |
1 files changed, 1195 insertions, 0 deletions
diff --git a/vgui2/src/vgui.cpp b/vgui2/src/vgui.cpp new file mode 100644 index 0000000..3508b1a --- /dev/null +++ b/vgui2/src/vgui.cpp @@ -0,0 +1,1195 @@ +//========= 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 ); +} |