//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Core implementation of vgui // // $NoKeywords: $ //===========================================================================// #if defined( WIN32 ) && !defined( _X360 ) #define WIN32_LEAN_AND_MEAN #include #endif #include "VGuiMatSurface/IMatSystemSurface.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 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 m_MessageQueue; // secondary message queue, holds all vgui messages generated by vgui CUtlLinkedList m_SecondaryQueue; // timing queue, holds all the messages that have to arrive at a specified time CUtlPriorityQueue 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 ); }