/* PCSX2 - PS2 Emulator for PCs * Copyright (C) 2002-2009 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. * * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with PCSX2. * If not, see . */ #include "PrecompiledHeader.h" #include "wxAppWithHelpers.h" #include "PersistentThread.h" DEFINE_EVENT_TYPE( pxEvt_DeleteObject ); DEFINE_EVENT_TYPE( pxEvt_DeleteThread ); DEFINE_EVENT_TYPE( pxEvt_StartIdleEventTimer ); DEFINE_EVENT_TYPE( pxEvt_InvokeAction ); DEFINE_EVENT_TYPE( pxEvt_SynchronousCommand ); IMPLEMENT_DYNAMIC_CLASS( pxSimpleEvent, wxEvent ) void BaseDeletableObject::DoDeletion() { wxAppWithHelpers* app = wxDynamicCast( wxApp::GetInstance(), wxAppWithHelpers ); pxAssume( app != NULL ); app->DeleteObject( *this ); } // -------------------------------------------------------------------------------------- // pxInvokeActionEvent Implementations // -------------------------------------------------------------------------------------- IMPLEMENT_DYNAMIC_CLASS( pxInvokeActionEvent, wxEvent ) pxInvokeActionEvent::pxInvokeActionEvent( SynchronousActionState* sema, int msgtype ) : wxEvent( 0, msgtype ) { m_state = sema; } pxInvokeActionEvent::pxInvokeActionEvent( SynchronousActionState& sema, int msgtype ) : wxEvent( 0, msgtype ) { m_state = &sema; } pxInvokeActionEvent::pxInvokeActionEvent( const pxInvokeActionEvent& src ) : wxEvent( src ) { m_state = src.m_state; } void pxInvokeActionEvent::SetException( const BaseException& ex ) { SetException( ex.Clone() ); } void pxInvokeActionEvent::SetException( BaseException* ex ) { const wxString& prefix( wxsFormat(L"(%s) ", GetClassInfo()->GetClassName()) ); ex->DiagMsg() = prefix + ex->DiagMsg(); if( !m_state ) { ScopedPtr exptr( ex ); // auto-delete it after handling. ex->Rethrow(); } m_state->SetException( ex ); } // -------------------------------------------------------------------------------------- // pxSynchronousCommandEvent // -------------------------------------------------------------------------------------- IMPLEMENT_DYNAMIC_CLASS( pxSynchronousCommandEvent, wxCommandEvent ) pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState* sema, wxEventType commandType, int winid) : wxCommandEvent( pxEvt_SynchronousCommand, winid ) { m_sync = sema; m_realEvent = commandType; } pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState& sema, wxEventType commandType, int winid) : wxCommandEvent( pxEvt_SynchronousCommand ) { m_sync = &sema; m_realEvent = commandType; } pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState* sema, const wxCommandEvent& evt ) : wxCommandEvent( evt ) { m_sync = sema; m_realEvent = evt.GetEventType(); SetEventType( pxEvt_SynchronousCommand ); } pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState& sema, const wxCommandEvent& evt ) : wxCommandEvent( evt ) { m_sync = &sema; m_realEvent = evt.GetEventType(); SetEventType( pxEvt_SynchronousCommand ); } pxSynchronousCommandEvent::pxSynchronousCommandEvent( const pxSynchronousCommandEvent& src ) : wxCommandEvent( src ) { m_sync = src.m_sync; m_realEvent = src.m_realEvent; } void pxSynchronousCommandEvent::SetException( const BaseException& ex ) { if( !m_sync ) ex.Rethrow(); m_sync->SetException( ex ); } void pxSynchronousCommandEvent::SetException( BaseException* ex ) { if( !m_sync ) { ScopedPtr exptr( ex ); // auto-delete it after handling. ex->Rethrow(); } m_sync->SetException( ex ); } // -------------------------------------------------------------------------------------- // pxInvokeMethodEvent // -------------------------------------------------------------------------------------- // Unlike pxPingEvent, the Semaphore belonging to this event is typically posted when the // invoked method is completed. If the method can be executed in non-blocking fashion then // it should leave the semaphore postback NULL. // class pxInvokeMethodEvent : public pxInvokeActionEvent { DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxInvokeMethodEvent) typedef pxInvokeActionEvent _parent; protected: void (*m_Method)(); public: virtual ~pxInvokeMethodEvent() throw() { } virtual pxInvokeMethodEvent *Clone() const { return new pxInvokeMethodEvent(*this); } explicit pxInvokeMethodEvent( void (*method)()=NULL, SynchronousActionState* sema=NULL ) : pxInvokeActionEvent( sema ) { m_Method = method; } explicit pxInvokeMethodEvent( void (*method)(), SynchronousActionState& sema ) : pxInvokeActionEvent( sema ) { m_Method = method; } pxInvokeMethodEvent( const pxInvokeMethodEvent& src ) : pxInvokeActionEvent( src ) { m_Method = src.m_Method; } void SetMethod( void (*method)() ) { m_Method = method; } protected: void _DoInvoke() { if( m_Method ) m_Method(); } }; IMPLEMENT_DYNAMIC_CLASS( pxInvokeMethodEvent, pxInvokeActionEvent ) // -------------------------------------------------------------------------------------- // pxExceptionEvent implementations // -------------------------------------------------------------------------------------- pxExceptionEvent::pxExceptionEvent( const BaseException& ex ) { m_except = ex.Clone(); } void pxExceptionEvent::_DoInvoke() { ScopedPtr deleteMe( m_except ); if( deleteMe ) deleteMe->Rethrow(); } // -------------------------------------------------------------------------------------- // wxAppWithHelpers Implementation // -------------------------------------------------------------------------------------- // // TODO : Ping dispatch and IdleEvent dispatch can be unified into a single dispatch, which // would mean checking only one list of events per idle event, instead of two. (ie, ping // events can be appended to the idle event list, instead of into their own custom list). // IMPLEMENT_DYNAMIC_CLASS( wxAppWithHelpers, wxApp ) // Posts a method to the main thread; non-blocking. Post occurs even when called from the // main thread. void wxAppWithHelpers::PostMethod( FnType_Void* method ) { PostEvent( pxInvokeMethodEvent( method ) ); } // Posts a method to the main thread; non-blocking. Post occurs even when called from the // main thread. void wxAppWithHelpers::PostIdleMethod( FnType_Void* method ) { pxInvokeMethodEvent evt( method ); AddIdleEvent( evt ); } bool wxAppWithHelpers::PostMethodMyself( void (*method)() ) { if( wxThread::IsMain() ) return false; PostEvent( pxInvokeMethodEvent( method ) ); return true; } void wxAppWithHelpers::ProcessMethod( void (*method)() ) { if( wxThread::IsMain() ) { method(); return; } SynchronousActionState sync; PostEvent( pxInvokeMethodEvent( method, sync ) ); sync.WaitForResult(); } void wxAppWithHelpers::PostEvent( const wxEvent& evt ) { // Const Cast is OK! // Truth is, AddPendingEvent should be a const-qualified parameter, as // it makes an immediate clone copy of the event -- but wxWidgets // fails again in structured C/C++ design design. So I'm forcing it as such // here. -- air _parent::AddPendingEvent( const_cast(evt) ); } bool wxAppWithHelpers::ProcessEvent( wxEvent& evt ) { // Note: We can't do an automatic blocking post of the message here, because wxWidgets // isn't really designed for it (some events return data to the caller via the event // struct, and posting the event would require a temporary clone, where changes would // be lost). AffinityAssert_AllowFrom_MainUI(); return _parent::ProcessEvent( evt ); } bool wxAppWithHelpers::ProcessEvent( wxEvent* evt ) { AffinityAssert_AllowFrom_MainUI(); ScopedPtr deleteMe( evt ); return _parent::ProcessEvent( *deleteMe ); } bool wxAppWithHelpers::ProcessEvent( pxInvokeActionEvent& evt ) { if( wxThread::IsMain() ) return _parent::ProcessEvent( evt ); else { SynchronousActionState sync; evt.SetSyncState( sync ); AddPendingEvent( evt ); sync.WaitForResult(); return true; } } bool wxAppWithHelpers::ProcessEvent( pxInvokeActionEvent* evt ) { if( wxThread::IsMain() ) { ScopedPtr deleteMe( evt ); return _parent::ProcessEvent( *deleteMe ); } else { SynchronousActionState sync; evt->SetSyncState( sync ); AddPendingEvent( *evt ); sync.WaitForResult(); return true; } } void wxAppWithHelpers::CleanUp() { // I'm pretty sure the message pump is dead by now, which means we need to run through // idle event list by hand and process the pending Deletion messages (all others can be // ignored -- it's only deletions we want handled, and really we *could* ignore those too // but I like to be tidy. -- air //IdleEventDispatcher( "CleanUp" ); //DeletionDispatcher(); _parent::CleanUp(); } void pxInvokeActionEvent::InvokeAction() { AffinityAssert_AllowFrom_MainUI(); try { _DoInvoke(); } catch( BaseException& ex ) { SetException( ex ); } catch( std::runtime_error& ex ) { SetException( new Exception::RuntimeError( ex ) ); } if( m_state ) m_state->PostResult(); } void wxAppWithHelpers::OnSynchronousCommand( pxSynchronousCommandEvent& evt ) { AffinityAssert_AllowFrom_MainUI(); evt.SetEventType( evt.GetRealEventType() ); try { ProcessEvent( evt ); } catch( BaseException& ex ) { evt.SetException( ex ); } catch( std::runtime_error& ex ) { evt.SetException( new Exception::RuntimeError( ex, evt.GetClassInfo()->GetClassName() ) ); } if( Semaphore* sema = evt.GetSemaphore() ) sema->Post(); } void wxAppWithHelpers::AddIdleEvent( const wxEvent& evt ) { ScopedLock lock( m_IdleEventMutex ); if( m_IdleEventQueue.size() == 0 ) PostEvent( pxSimpleEvent( pxEvt_StartIdleEventTimer ) ); m_IdleEventQueue.push_back( evt.Clone() ); } void wxAppWithHelpers::OnStartIdleEventTimer( wxEvent& evt ) { ScopedLock lock( m_IdleEventMutex ); if( m_IdleEventQueue.size() != 0 ) m_IdleEventTimer.Start( 100, true ); } void wxAppWithHelpers::IdleEventDispatcher( const char* action ) { ScopedLock lock( m_IdleEventMutex ); size_t size = m_IdleEventQueue.size(); if( size == 0 ) return; DbgCon.WriteLn( Color_Gray, "App IdleQueue (%s) -> %u events.", action, size ); for( size_t i=0; i thr( (PersistentThread*)evt.GetClientData() ); if( !thr ) return; thr->RethrowException(); } wxAppWithHelpers::wxAppWithHelpers() : m_IdleEventTimer( this ) { #ifdef __WXMSW__ // This variable assignment ensures that MSVC links in the TLS setup stubs even in // full optimization builds. Without it, DLLs that use TLS won't work because the // FS segment register won't have been initialized by the main exe, due to tls_insurance // being optimized away >_< --air static __threadlocal int tls_insurance = 0; tls_insurance = 1; #endif }