A big old crapload of UI and thread management changes. Recoded the EventSource/EventListener system to be more C++ and less hacky C. Probably breaks Linux!

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2474 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2010-01-22 15:22:01 +00:00
parent c8fa4ffd45
commit e747337d63
66 changed files with 2157 additions and 1137 deletions

View File

@ -275,6 +275,10 @@
RelativePath="..\..\src\Utilities\vssprintf.cpp"
>
</File>
<File
RelativePath="..\..\src\Utilities\wxAppWithHelpers.cpp"
>
</File>
<File
RelativePath="..\..\src\Utilities\wxGuiTools.cpp"
>
@ -497,6 +501,10 @@
RelativePath="..\..\include\Utilities\Path.h"
>
</File>
<File
RelativePath="..\..\include\Utilities\PersistentThread.h"
>
</File>
<File
RelativePath="..\..\src\Utilities\PrecompiledHeader.h"
>
@ -533,6 +541,10 @@
RelativePath="..\..\include\Utilities\win_memzero.h"
>
</File>
<File
RelativePath="..\..\include\Utilities\wxAppWithHelpers.h"
>
</File>
<File
RelativePath="..\..\include\Utilities\wxBaseTools.h"
>

View File

@ -18,63 +18,17 @@
#include <list>
#include "Threading.h"
class wxCommandEvent;
// __evt_fastcall : Work-around for a GCC 4.3 compilation bug. The templated FuncType
// throws type mismatches if we have a __fastcall qualifier. >_< --air
#if defined( __GNUC__ ) && (__GNUC__ < 4 ) || ((__GNUC__ == 4) && ( __GNUC_MINOR__ <= 3 ))
# define __evt_fastcall
#else
# define __evt_fastcall __fastcall
#endif
// --------------------------------------------------------------------------------------
// EventListener< typename EvtType >
// --------------------------------------------------------------------------------------
template< typename EvtType >
struct EventListener
{
typedef void __evt_fastcall FuncType( void* object, EvtType& evt );
void* object;
FuncType* OnEvent;
EventListener( FuncType* fnptr )
{
object = NULL;
OnEvent = fnptr;
}
EventListener( void* objhandle, FuncType* fnptr )
{
object = objhandle;
OnEvent = fnptr;
}
bool operator ==( const EventListener& right ) const
{
return (object == right.object) && (OnEvent == right.OnEvent);
}
bool operator !=( const EventListener& right ) const
{
return this->operator ==(right);
}
};
// --------------------------------------------------------------------------------------
// EventSource< template EvtType >
// --------------------------------------------------------------------------------------
template< typename EvtType >
template< typename ListenerType >
class EventSource
{
public:
typedef EventListener< EvtType > ListenerType;
typedef typename std::list< ListenerType > ListenerList;
typedef typename ListenerList::iterator Handle;
typedef typename ListenerType::EvtParams EvtParams;
typedef typename std::list< ListenerType* > ListenerList;
typedef typename ListenerList::iterator ListenerIterator;
protected:
typedef typename ListenerList::const_iterator ConstIterator;
@ -90,27 +44,34 @@ protected:
Threading::Mutex m_listeners_lock;
public:
EventSource() : m_cache_valid( false )
EventSource()
{
m_cache_valid = false;
}
virtual ~EventSource() throw() {}
virtual void Remove( const ListenerType& listener );
virtual void Remove( const Handle& listenerHandle );
virtual ListenerIterator Add( ListenerType& listener );
virtual void Remove( ListenerType& listener );
virtual void Remove( const ListenerIterator& listenerHandle );
void Add( ListenerType* listener )
{
if( listener == NULL ) return;
Add( *listener );
}
void Remove( ListenerType* listener )
{
if( listener == NULL ) return;
Remove( *listener );
}
Handle AddFast( const ListenerType& listener );
void Add( void* objhandle, typename ListenerType::FuncType* fnptr );
void Remove( void* objhandle, typename ListenerType::FuncType* fnptr );
// Checks for duplicates before adding the event.
virtual void Add( const ListenerType& listener );
virtual void RemoveObject( const void* object );
void Dispatch( EvtType& evt );
void Dispatch( const EvtParams& params );
protected:
virtual Handle _AddFast_without_lock( const ListenerType& listener );
inline void _DispatchRaw( ConstIterator iter, const ConstIterator& iend, EvtType& evt );
virtual ListenerIterator _AddFast_without_lock( ListenerType& listener );
virtual void _DispatchRaw( ListenerIterator iter, const ListenerIterator& iend, const EvtParams& params );
};
// --------------------------------------------------------------------------------------
@ -118,25 +79,26 @@ protected:
// --------------------------------------------------------------------------------------
// Encapsulated event listener binding, provides the "benefits" of object unwinding.
//
template< typename EvtType >
template< typename ListenerType >
class EventListenerBinding
{
public:
typedef typename EventSource<EvtType>::ListenerType ListenerHandle;
typedef typename EventSource<EvtType>::Handle ConstIterator;
typedef typename EventSource<ListenerType> EventSourceType;
typedef typename EventSource<ListenerType>::ListenerIterator ListenerIterator;
protected:
EventSource<EvtType>& m_source;
const ListenerHandle m_listener;
ConstIterator m_iter;
bool m_attached;
EventSourceType& m_source;
const ListenerType m_listener;
ListenerIterator m_iter;
bool m_attached;
public:
EventListenerBinding( EventSource<EvtType>& source, const ListenerHandle& listener, bool autoAttach=true )
EventListenerBinding( EventSourceType& source, ListenerType& listener, bool autoAttach=true )
: m_source( source )
, m_listener( listener )
, m_attached( false )
{
m_attached = false;
// If you want to assert on null pointers, you'll need to do the check yourself. There's
// too many cases where silently ignoring null pointers is the desired behavior.
//if( !pxAssertDev( listener.OnEvent != NULL, "NULL listener callback function." ) ) return;
@ -158,13 +120,24 @@ public:
void Attach()
{
if( m_attached || (m_listener.OnEvent == NULL) ) return;
m_iter = m_source.AddFast( m_listener );
m_iter = m_source.Add( m_listener );
m_attached = true;
}
};
typedef EventSource<wxCommandEvent> CmdEvt_Source;
typedef EventListener<wxCommandEvent> CmdEvt_Listener;
typedef EventListenerBinding<wxCommandEvent> CmdEvt_ListenerBinding;
// --------------------------------------------------------------------------------------
// IEventDispatcher
// --------------------------------------------------------------------------------------
// This class is used as a base interface for EventListeners. It allows the listeners to do
// customized dispatching of several event types into "user friendly" function overrides.
//
template< typename EvtParams >
class IEventDispatcher
{
protected:
IEventDispatcher() {}
#define EventSource_ImplementType( tname ) template class EventSource<tname>
public:
virtual ~IEventDispatcher() throw() {}
virtual void DispatchEvent( const EvtParams& params )=0;
};

View File

@ -17,116 +17,81 @@
using Threading::ScopedLock;
template< typename EvtType >
class PredicatesAreTheThingsOfNightmares
{
typedef EventListener< EvtType > ListenerType;
protected:
const void* const m_object_match;
public:
PredicatesAreTheThingsOfNightmares( const void* objmatch ) : m_object_match( objmatch ) { }
bool operator()( const ListenerType& src ) const
{
return src.object == m_object_match;
}
};
// Checks for duplicates before adding the event.
template< typename EvtType >
void EventSource<EvtType>::Add( const ListenerType& listener )
template< typename ListenerType >
typename EventSource<ListenerType>::ListenerIterator EventSource<ListenerType>::Add( ListenerType& listener )
{
ScopedLock locker( m_listeners_lock );
if( !pxAssertDev( listener.OnEvent != NULL, "NULL listener callback function." ) ) return;
Handle iter = m_listeners.begin();
while( iter != m_listeners.end() )
// Check for duplicates before adding the event.
if( IsDebugBuild )
{
if( *iter == listener ) return;
++iter;
ListenerIterator iter = m_listeners.begin();
while( iter != m_listeners.end() )
{
if( (*iter) == &listener ) return iter;
++iter;
}
}
_AddFast_without_lock( listener );
return _AddFast_without_lock( listener );
}
template< typename EvtType >
void EventSource<EvtType>::Remove( const ListenerType& listener )
template< typename ListenerType >
void EventSource<ListenerType>::Remove( ListenerType& listener )
{
ScopedLock locker( m_listeners_lock );
m_cache_valid = false;
m_listeners.remove( listener );
m_listeners.remove( &listener );
}
template< typename EvtType >
void EventSource<EvtType>::Remove( const Handle& listenerHandle )
template< typename ListenerType >
void EventSource<ListenerType>::Remove( const ListenerIterator& listenerHandle )
{
ScopedLock locker( m_listeners_lock );
m_cache_valid = false;
m_listeners.erase( listenerHandle );
}
template< typename EvtType >
typename EventSource<EvtType>::Handle EventSource<EvtType>::AddFast( const ListenerType& listener )
{
ScopedLock locker( m_listeners_lock );
return _AddFast_without_lock( listener );
}
template< typename EvtType >
typename EventSource<EvtType>::Handle EventSource<EvtType>::_AddFast_without_lock( const ListenerType& listener )
template< typename ListenerType >
typename EventSource<ListenerType>::ListenerIterator EventSource<ListenerType>::_AddFast_without_lock( ListenerType& listener )
{
m_cache_valid = false;
m_listeners.push_front( listener );
m_listeners.push_front( &listener );
return m_listeners.begin();
}
template< typename EvtType >
void EventSource<EvtType>::Add( void* objhandle, typename ListenerType::FuncType* fnptr )
{
Add( ListenerType( objhandle, fnptr ) );
}
template< typename EvtType >
void EventSource<EvtType>::Remove( void* objhandle, typename ListenerType::FuncType* fnptr )
{
Remove( ListenerType( objhandle, fnptr ) );
}
// removes all listeners which reference the given object. Use for assuring object deletion.
template< typename EvtType >
void EventSource<EvtType>::RemoveObject( const void* object )
{
m_cache_valid = false;
m_listeners.remove_if( PredicatesAreTheThingsOfNightmares<EvtType>( object ) );
}
template< typename EvtType >
__forceinline void EventSource<EvtType>::_DispatchRaw( ConstIterator iter, const ConstIterator& iend, EvtType& evt )
template< typename ListenerType >
__forceinline void EventSource<ListenerType>::_DispatchRaw( ListenerIterator iter, const ListenerIterator& iend, const EvtParams& evtparams )
{
while( iter != iend )
{
try
{
iter->OnEvent( iter->object, evt );
try {
(*iter)->DispatchEvent( evtparams );
}
catch( Exception::RuntimeError& ex )
{
Console.Error( L"Ignoring runtime error thrown from event listener: " + ex.FormatDiagnosticMessage() );
if( IsDevBuild ) {
pxFailDev( L"Ignoring runtime error thrown from event listener (event listeners should not throw exceptions!): " + ex.FormatDiagnosticMessage() );
}
else {
Console.Error( L"Ignoring runtime error thrown from event listener: " + ex.FormatDiagnosticMessage() );
}
}
catch( Exception::BaseException& ex )
{
if( IsDevBuild ) throw;
if( IsDevBuild )
{
ex.DiagMsg() = L"Non-runtime BaseException thrown from event listener .. " + ex.DiagMsg();
throw;
}
Console.Error( L"Ignoring non-runtime BaseException thrown from event listener: " + ex.FormatDiagnosticMessage() );
}
++iter;
}
}
template< typename EvtType >
void EventSource<EvtType>::Dispatch( EvtType& evt )
template< typename ListenerType >
void EventSource<ListenerType>::Dispatch( const EvtParams& evtparams )
{
if( !m_cache_valid )
{
@ -134,5 +99,6 @@ void EventSource<EvtType>::Dispatch( EvtType& evt )
m_cache_valid = true;
}
_DispatchRaw( m_cache_copy.begin(), m_cache_copy.end(), evt );
if( m_cache_copy.empty() ) return;
_DispatchRaw( m_cache_copy.begin(), m_cache_copy.end(), evtparams );
}

View File

@ -238,7 +238,7 @@ namespace Exception
class HardwareDeficiency : public virtual RuntimeError
{
public:
DEFINE_RUNTIME_EXCEPTION( HardwareDeficiency, wxLt("Your machine's hardware is incapable of running Pcsx2. Sorry dood.") );
DEFINE_RUNTIME_EXCEPTION( HardwareDeficiency, wxLt("Your machine's hardware is incapable of running PCSX2. Sorry dood.") );
};
// ---------------------------------------------------------------------------------------

View File

@ -15,6 +15,20 @@
#pragma once
// This macro is actually useful for about any and every possible application of C++
// equality operators.
#define OpEqu( field ) (field == right.field)
// Macro used for removing some of the redtape involved in defining bitfield/union helpers.
//
#define BITFIELD32() \
union { \
u32 bitset; \
struct {
#define BITFIELD_END }; };
// ----------------------------------------------------------------------------------------
// RecursionGuard - Basic protection against function recursion
// ----------------------------------------------------------------------------------------
@ -39,6 +53,54 @@ public:
bool IsReentrant() const { return Counter > 1; }
};
// --------------------------------------------------------------------------------------
// IDeletableObject
// --------------------------------------------------------------------------------------
// Oh the fruits and joys of multithreaded C++ coding conundrums! This class provides a way
// to be deleted from arbitraty threads, or to delete themselves (which is considered unsafe
// in C++, though it does typically work). It also gives objects a second recourse for
// doing fully virtualized cleanup, something C++ also makes impossible because of how it
// implements it's destructor hierarchy.
//
// To utilize virtual destruction, override DoDeletion() and be sure to invoke the base class
// implementation of DoDeletion().
//
// Assertions:
// This class generates an assertion of the destructor is called from anything other than
// the main/gui thread.
//
// Rationale:
// wxWidgets provides a pending deletion feature, but it's specific to wxCore (not wxBase)
// which means it requires wxApp and all that, which is bad for plugins and the possibility
// of linking PCSX2 core against a non-WX gui in the future. It's also not thread safe
// (sigh). And, finally, it requires quite a bit of red tape to implement wxObjects because
// of the wx-custom runtime type information. So I made my own.
//
class IDeletableObject
{
protected:
volatile long m_IsBeingDeleted;
public:
IDeletableObject();
virtual ~IDeletableObject() throw();
void DeleteSelf();
bool IsBeingDeleted() { return !!m_IsBeingDeleted; }
// Returns FALSE if the object is already marked for deletion, or TRUE if the app
// should schedule the object for deletion. Only schedule if TRUE is returned, otherwise
// the object could get deleted twice if two threads try to schedule it at the same time.
bool MarkForDeletion();
protected:
// This function is GUI implementation dependent! It's implemented by PCSX2's AppHost,
// but if the SysCore is being linked to another front end, you'll need to implement this
// yourself. Most GUIs have built in message pumps. If a platform lacks one then you'll
// need to implement one yourself (yay?).
virtual void DoDeletion();
};
enum PageProtectionMode
{
@ -47,11 +109,11 @@ enum PageProtectionMode
Protect_ReadWrite
};
//////////////////////////////////////////////////////////////////////////////////////////
// HostSys - Namespace housing general system-level implementations relating to loading
// plugins and allocating memory. For now, these functions are all accessed via Sys*
// versions defined in System.h/cpp.
//
// --------------------------------------------------------------------------------------
// HostSys
// --------------------------------------------------------------------------------------
// (this namespace name sucks, and is a throw-back to an older attempt to make things cross
// platform prior to wxWidgets .. it should prolly be removed -- air)
namespace HostSys
{
// Maps a block of memory for use as a recompiled code buffer.
@ -74,9 +136,6 @@ namespace HostSys
}
//////////////////////////////////////////////////////////////////////////////////////////
extern void InitCPUTicks();
extern u64 GetTickFrequency();
extern u64 GetCPUTicks();

View File

@ -0,0 +1,285 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "Threading.h"
#include "EventSource.h"
namespace Threading
{
// --------------------------------------------------------------------------------------
// IThread - Interface for the public access to PersistentThread.
// --------------------------------------------------------------------------------------
// Class usage: Can be used for allowing safe nullification of a thread handle. Rather
// than being NULL'd, the handle can be mapped to an IThread implementation which acts
// as a do-nothing placebo or an assertion generator.
//
class IThread
{
DeclareNoncopyableObject(IThread);
public:
IThread() {}
virtual ~IThread() throw() {}
virtual bool IsSelf() const { return false; }
virtual bool IsRunning() { return false; }
virtual void Start() {}
virtual void Cancel( bool isBlocking = true ) {}
virtual void Block() {}
virtual bool Detach() { return false; }
};
// --------------------------------------------------------------------------------------
// ThreadDeleteEvent
// --------------------------------------------------------------------------------------
class EventListener_Thread : public IEventDispatcher<int>
{
public:
typedef int EvtParams;
protected:
PersistentThread* m_thread;
public:
EventListener_Thread()
{
m_thread = NULL;
}
virtual ~EventListener_Thread() throw() {}
void SetThread( PersistentThread& thr ) { m_thread = &thr; }
void SetThread( PersistentThread* thr ) { m_thread = thr; }
void DispatchEvent( const int& params )
{
OnThreadCleanup();
}
protected:
// Invoked by the PersistentThread when the thread execution is ending. This is
// typically more useful than a delete listener since the extended thread information
// provided by virtualized functions/methods will be available.
// Important! This event is executed *by the thread*, so care must be taken to ensure
// thread sync when necessary (posting messages to the main thread, etc).
virtual void OnThreadCleanup()=0;
};
// --------------------------------------------------------------------------------------
// PersistentThread - Helper class for the basics of starting/managing persistent threads.
// --------------------------------------------------------------------------------------
// This class is meant to be a helper for the typical threading model of "start once and
// reuse many times." This class incorporates a lot of extra overhead in stopping and
// starting threads, but in turn provides most of the basic thread-safety and event-handling
// functionality needed for a threaded operation. In practice this model is usually an
// ideal one for efficiency since Operating Systems themselves typically subscribe to a
// design where sleeping, suspending, and resuming threads is very efficient, but starting
// new threads has quite a bit of overhead.
//
// To use this as a base class for your threaded procedure, overload the following virtual
// methods:
// void OnStart();
// void ExecuteTaskInThread();
// void OnCleanupInThread();
//
// Use the public methods Start() and Cancel() to start and shutdown the thread, and use
// m_sem_event internally to post/receive events for the thread (make a public accessor for
// it in your derived class if your thread utilizes the post).
//
// Notes:
// * Constructing threads as static global vars isn't recommended since it can potentially
// confuse w32pthreads, if the static initializers are executed out-of-order (C++ offers
// no dependency options for ensuring correct static var initializations). Use heap
// allocation to create thread objects instead.
//
class PersistentThread : public virtual IThread
{
DeclareNoncopyableObject(PersistentThread);
friend void pxYield( int ms );
protected:
wxString m_name; // diagnostic name for our thread.
pthread_t m_thread;
Semaphore m_sem_event; // general wait event that's needed by most threads
Semaphore m_sem_startup; // startup sync tool
Mutex m_lock_InThread; // used for canceling and closing threads in a deadlock-safe manner
MutexLockRecursive m_lock_start; // used to lock the Start() code from starting simultaneous threads accidentally.
volatile long m_detached; // a boolean value which indicates if the m_thread handle is valid
volatile long m_running; // set true by Start(), and set false by Cancel(), Block(), etc.
// exception handle, set non-NULL if the thread terminated with an exception
// Use RethrowException() to re-throw the exception using its original exception type.
ScopedPtr<Exception::BaseException> m_except;
EventSource<EventListener_Thread> m_evtsrc_OnDelete;
public:
virtual ~PersistentThread() throw();
PersistentThread();
PersistentThread( const char* name );
pthread_t GetId() const { return m_thread; }
virtual void Start();
virtual void Cancel( bool isBlocking = true );
virtual bool Cancel( const wxTimeSpan& timeout );
virtual bool Detach();
virtual void Block();
virtual void RethrowException() const;
void AddListener( EventListener_Thread& evt );
void AddListener( EventListener_Thread* evt )
{
if( evt == NULL ) return;
AddListener( *evt );
}
void WaitOnSelf( Semaphore& mutex ) const;
void WaitOnSelf( Mutex& mutex ) const;
bool WaitOnSelf( Semaphore& mutex, const wxTimeSpan& timeout ) const;
bool WaitOnSelf( Mutex& mutex, const wxTimeSpan& timeout ) const;
bool IsRunning() const;
bool IsSelf() const;
wxString GetName() const;
bool HasPendingException() const { return !!m_except; }
protected:
// Extending classes should always implement your own OnStart(), which is called by
// Start() once necessary locks have been obtained. Do not override Start() directly
// unless you're really sure that's what you need to do. ;)
virtual void OnStart();
virtual void OnStartInThread();
// This is called when the thread has been canceled or exits normally. The PersistentThread
// automatically binds it to the pthread cleanup routines as soon as the thread starts.
virtual void OnCleanupInThread();
// Implemented by derived class to perform actual threaded task!
virtual void ExecuteTaskInThread()=0;
void TestCancel() const;
// Yields this thread to other threads and checks for cancellation. A sleeping thread should
// always test for cancellation, however if you really don't want to, you can use Threading::Sleep()
// or better yet, disable cancellation of the thread completely with DisableCancellation().
//
// Parameters:
// ms - 'minimum' yield time in milliseconds (rough -- typically yields are longer by 1-5ms
// depending on operating system/platform). If ms is 0 or unspecified, then a single
// timeslice is yielded to other contending threads. If no threads are contending for
// time when ms==0, then no yield is done, but cancellation is still tested.
void Yield( int ms = 0 )
{
pxAssert( IsSelf() );
Threading::Sleep( ms );
TestCancel();
}
void FrankenMutex( Mutex& mutex );
bool AffinityAssert_AllowFromSelf( const DiagnosticOrigin& origin ) const;
bool AffinityAssert_DisallowFromSelf( const DiagnosticOrigin& origin ) const;
// ----------------------------------------------------------------------------
// Section of methods for internal use only.
bool _basecancel();
void _selfRunningTest( const wxChar* name ) const;
void _DoSetThreadName( const wxString& name );
void _DoSetThreadName( const char* name );
void _internal_execute();
void _try_virtual_invoke( void (PersistentThread::*method)() );
void _ThreadCleanup();
static void* _internal_callback( void* func );
static void _pt_callback_cleanup( void* handle );
};
// --------------------------------------------------------------------------------------
// BaseTaskThread
// --------------------------------------------------------------------------------------
// an abstract base class which provides simple parallel execution of single tasks.
//
// FIXME: This class is incomplete and untested! Don't use, unless you want to fix it
// while you're at it. :D
//
// Implementation:
// To use this class your derived class will need to implement its own Task() function
// and also a "StartTask( parameters )" function which suits the need of your task, along
// with any local variables your task needs to do its job. You may additionally want to
// implement a "GetResult()" function, which would be a combination of WaitForResult()
// and a return value of the computational result.
//
// Thread Safety:
// If operating on local variables, you must execute WaitForResult() before leaving the
// variable scope -- or alternatively have your StartTask() implementation make full
// copies of dependent data. Also, by default PostTask() always assumes the previous
// task has completed. If your system can post a new task before the previous one has
// completed, then it needs to explicitly call WaitForResult() or provide a mechanism
// to cancel the previous task (which is probably more work than it's worth).
//
// Performance notes:
// * Remember that thread creation is generally slow, so you should make your object
// instance once early and then feed it tasks repeatedly over the course of program
// execution.
//
// * For threading to be a successful speedup, the task being performed should be as lock
// free as possible. For example using STL containers in parallel usually fails to
// yield any speedup due to the gratuitous amount of locking that the STL performs
// internally.
//
// * The best application of tasking threads is to divide a large loop over a linear array
// into smaller sections. For example, if you have 20,000 items to process, the task
// can be divided into two threads of 10,000 items each.
//
class BaseTaskThread : public PersistentThread
{
protected:
volatile bool m_Done;
volatile bool m_TaskPending;
Semaphore m_post_TaskComplete;
Mutex m_lock_TaskComplete;
public:
virtual ~BaseTaskThread() throw() {}
BaseTaskThread() :
m_Done( false )
, m_TaskPending( false )
, m_post_TaskComplete()
{
}
void Block();
void PostTask();
void WaitForResult();
protected:
// Abstract method run when a task has been posted. Implementing classes should do
// all your necessary processing work here.
virtual void Task()=0;
virtual void ExecuteTaskInThread();
};
}

View File

@ -112,7 +112,7 @@ namespace Exception
#if wxUSE_GUI
// --------------------------------------------------------------------------------------
// ThreadTimedOut Exception
// ThreadDeadlock Exception
// --------------------------------------------------------------------------------------
// This exception is thrown by Semaphore and Mutex Wait/Acquire functions if a blocking wait is
// needed due to gui Yield recursion, and the timeout period for deadlocking (usually 3 seconds)
@ -121,24 +121,24 @@ namespace Exception
// * If the user-specified timeout is less than the deadlock timeout.
// * If the method is run from a thread *other* than the MainGui thread.
//
class ThreadTimedOut : public virtual BaseThreadError
class ThreadDeadlock : public virtual BaseThreadError
{
public:
DEFINE_EXCEPTION_COPYTORS( ThreadTimedOut )
DEFINE_EXCEPTION_COPYTORS( ThreadDeadlock )
explicit ThreadTimedOut( Threading::PersistentThread* _thread=NULL, const char* msg="Blocking action timed out waiting for '%s' (potential thread deadlock)." )
explicit ThreadDeadlock( Threading::PersistentThread* _thread=NULL, const char* msg="Blocking action timed out waiting for '%s' (potential thread deadlock)." )
{
m_thread = _thread;
BaseException::InitBaseEx( msg );
}
ThreadTimedOut( Threading::PersistentThread& _thread, const char* msg="Blocking action timed out waiting for '%s' (potential thread deadlock)." )
ThreadDeadlock( Threading::PersistentThread& _thread, const char* msg="Blocking action timed out waiting for '%s' (potential thread deadlock)." )
{
m_thread = &_thread;
BaseException::InitBaseEx( msg );
}
ThreadTimedOut( Threading::PersistentThread& _thread, const wxString& msg_diag, const wxString& msg_user )
ThreadDeadlock( Threading::PersistentThread& _thread, const wxString& msg_diag, const wxString& msg_user )
{
m_thread = &_thread;
BaseException::InitBaseEx( msg_diag, msg_user );
@ -310,154 +310,6 @@ namespace Threading
virtual bool IsRecursive() const { return true; }
};
// --------------------------------------------------------------------------------------
// IThread - Interface for the public access to PersistentThread.
// --------------------------------------------------------------------------------------
// Class usage: Can be used for allowing safe nullification of a thread handle. Rather
// than being NULL'd, the handle can be mapped to an IThread implementation which acts
// as a do-nothing placebo or an assertion generator.
//
class IThread
{
DeclareNoncopyableObject(IThread);
public:
IThread() {}
virtual ~IThread() throw() {}
virtual bool IsSelf() const { return false; }
virtual bool IsRunning() { return false; }
virtual void Start() {}
virtual void Cancel( bool isBlocking = true ) {}
virtual void Block() {}
virtual bool Detach() { return false; }
};
// --------------------------------------------------------------------------------------
// PersistentThread - Helper class for the basics of starting/managing persistent threads.
// --------------------------------------------------------------------------------------
// This class is meant to be a helper for the typical threading model of "start once and
// reuse many times." This class incorporates a lot of extra overhead in stopping and
// starting threads, but in turn provides most of the basic thread-safety and event-handling
// functionality needed for a threaded operation. In practice this model is usually an
// ideal one for efficiency since Operating Systems themselves typically subscribe to a
// design where sleeping, suspending, and resuming threads is very efficient, but starting
// new threads has quite a bit of overhead.
//
// To use this as a base class for your threaded procedure, overload the following virtual
// methods:
// void OnStart();
// void ExecuteTaskInThread();
// void OnCleanupInThread();
//
// Use the public methods Start() and Cancel() to start and shutdown the thread, and use
// m_sem_event internally to post/receive events for the thread (make a public accessor for
// it in your derived class if your thread utilizes the post).
//
// Notes:
// * Constructing threads as static global vars isn't recommended since it can potentially
// confuse w32pthreads, if the static initializers are executed out-of-order (C++ offers
// no dependency options for ensuring correct static var initializations). Use heap
// allocation to create thread objects instead.
//
class PersistentThread : public virtual IThread
{
DeclareNoncopyableObject(PersistentThread);
friend void pxYield( int ms );
protected:
wxString m_name; // diagnostic name for our thread.
pthread_t m_thread;
Semaphore m_sem_event; // general wait event that's needed by most threads
Semaphore m_sem_startup; // startup sync tool
Mutex m_lock_InThread; // used for canceling and closing threads in a deadlock-safe manner
MutexLockRecursive m_lock_start; // used to lock the Start() code from starting simultaneous threads accidentally.
volatile long m_detached; // a boolean value which indicates if the m_thread handle is valid
volatile long m_running; // set true by Start(), and set false by Cancel(), Block(), etc.
// exception handle, set non-NULL if the thread terminated with an exception
// Use RethrowException() to re-throw the exception using its original exception type.
ScopedPtr<Exception::BaseException> m_except;
public:
virtual ~PersistentThread() throw();
PersistentThread();
PersistentThread( const char* name );
virtual void Start();
virtual void Cancel( bool isBlocking = true );
virtual bool Cancel( const wxTimeSpan& timeout );
virtual bool Detach();
virtual void Block();
virtual void RethrowException() const;
void WaitOnSelf( Semaphore& mutex ) const;
void WaitOnSelf( Mutex& mutex ) const;
bool WaitOnSelf( Semaphore& mutex, const wxTimeSpan& timeout ) const;
bool WaitOnSelf( Mutex& mutex, const wxTimeSpan& timeout ) const;
bool IsRunning() const;
bool IsSelf() const;
wxString GetName() const;
bool HasPendingException() const { return !!m_except; }
protected:
// Extending classes should always implement your own OnStart(), which is called by
// Start() once necessary locks have been obtained. Do not override Start() directly
// unless you're really sure that's what you need to do. ;)
virtual void OnStart();
virtual void OnStartInThread();
// This is called when the thread has been canceled or exits normally. The PersistentThread
// automatically binds it to the pthread cleanup routines as soon as the thread starts.
virtual void OnCleanupInThread();
// Implemented by derived class to perform actual threaded task!
virtual void ExecuteTaskInThread()=0;
void TestCancel() const;
// Yields this thread to other threads and checks for cancellation. A sleeping thread should
// always test for cancellation, however if you really don't want to, you can use Threading::Sleep()
// or better yet, disable cancellation of the thread completely with DisableCancellation().
//
// Parameters:
// ms - 'minimum' yield time in milliseconds (rough -- typically yields are longer by 1-5ms
// depending on operating system/platform). If ms is 0 or unspecified, then a single
// timeslice is yielded to other contending threads. If no threads are contending for
// time when ms==0, then no yield is done, but cancellation is still tested.
void Yield( int ms = 0 )
{
pxAssert( IsSelf() );
Threading::Sleep( ms );
TestCancel();
}
void FrankenMutex( Mutex& mutex );
bool AffinityAssert_AllowFromSelf( const DiagnosticOrigin& origin ) const;
bool AffinityAssert_DisallowFromSelf( const DiagnosticOrigin& origin ) const;
// ----------------------------------------------------------------------------
// Section of methods for internal use only.
bool _basecancel();
void _selfRunningTest( const wxChar* name ) const;
void _DoSetThreadName( const wxString& name );
void _DoSetThreadName( const char* name );
void _internal_execute();
void _try_virtual_invoke( void (PersistentThread::*method)() );
void _ThreadCleanup();
static void* _internal_callback( void* func );
static void _pt_callback_cleanup( void* handle );
};
// --------------------------------------------------------------------------------------
// ScopedLock
// --------------------------------------------------------------------------------------
@ -471,7 +323,7 @@ namespace Threading
protected:
Mutex& m_lock;
bool m_IsLocked;
bool m_IsLocked;
public:
virtual ~ScopedLock() throw()
@ -482,8 +334,8 @@ namespace Threading
ScopedLock( Mutex& locker ) :
m_lock( locker )
, m_IsLocked( true )
{
m_IsLocked = true;
m_lock.Acquire();
}
@ -509,8 +361,8 @@ namespace Threading
// Special constructor used by ScopedTryLock
ScopedLock( Mutex& locker, bool isTryLock ) :
m_lock( locker )
, m_IsLocked( isTryLock ? m_lock.TryAcquire() : false )
{
m_IsLocked = isTryLock ? m_lock.TryAcquire() : false;
}
};
@ -551,71 +403,5 @@ namespace Threading
bool Failed() const { return !m_IsLocked; }
};
// --------------------------------------------------------------------------------------
// BaseTaskThread
// --------------------------------------------------------------------------------------
// an abstract base class which provides simple parallel execution of single tasks.
//
// FIXME: This class is incomplete and untested! Don't use, unless you want to fix it
// while you're at it. :D
//
// Implementation:
// To use this class your derived class will need to implement its own Task() function
// and also a "StartTask( parameters )" function which suits the need of your task, along
// with any local variables your task needs to do its job. You may additionally want to
// implement a "GetResult()" function, which would be a combination of WaitForResult()
// and a return value of the computational result.
//
// Thread Safety:
// If operating on local variables, you must execute WaitForResult() before leaving the
// variable scope -- or alternatively have your StartTask() implementation make full
// copies of dependent data. Also, by default PostTask() always assumes the previous
// task has completed. If your system can post a new task before the previous one has
// completed, then it needs to explicitly call WaitForResult() or provide a mechanism
// to cancel the previous task (which is probably more work than it's worth).
//
// Performance notes:
// * Remember that thread creation is generally slow, so you should make your object
// instance once early and then feed it tasks repeatedly over the course of program
// execution.
//
// * For threading to be a successful speedup, the task being performed should be as lock
// free as possible. For example using STL containers in parallel usually fails to
// yield any speedup due to the gratuitous amount of locking that the STL performs
// internally.
//
// * The best application of tasking threads is to divide a large loop over a linear array
// into smaller sections. For example, if you have 20,000 items to process, the task
// can be divided into two threads of 10,000 items each.
//
class BaseTaskThread : public PersistentThread
{
protected:
volatile bool m_Done;
volatile bool m_TaskPending;
Semaphore m_post_TaskComplete;
Mutex m_lock_TaskComplete;
public:
virtual ~BaseTaskThread() throw() {}
BaseTaskThread() :
m_Done( false )
, m_TaskPending( false )
, m_post_TaskComplete()
{
}
void Block();
void PostTask();
void WaitForResult();
protected:
// Abstract method run when a task has been posted. Implementing classes should do
// all your necessary processing work here.
virtual void Task()=0;
virtual void ExecuteTaskInThread();
};
}

View File

@ -17,9 +17,8 @@
#include <wx/wx.h>
#include "IniInterface.h"
#include "Utilities/Threading.h"
#include "Utilities/wxGuiTools.h"
#include "Threading.h"
#include "wxGuiTools.h"
using namespace Threading;
@ -29,26 +28,10 @@ class pxMessageBoxEvent;
BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE( pxEvt_Ping, -1 )
DECLARE_EVENT_TYPE( pxEvt_MessageBox, -1 )
DECLARE_EVENT_TYPE( pxEvt_Assertion, -1 )
DECLARE_EVENT_TYPE( pxEvt_DeleteObject, -1 )
//DECLARE_EVENT_TYPE( pxEvt_Assertion, -1 )
END_DECLARE_EVENT_TYPES()
// --------------------------------------------------------------------------------------
// AppIniSaver / AppIniLoader
// --------------------------------------------------------------------------------------
class AppIniSaver : public IniSaver
{
public:
AppIniSaver();
virtual ~AppIniSaver() throw() {}
};
class AppIniLoader : public IniLoader
{
public:
AppIniLoader();
virtual ~AppIniLoader() throw() {}
};
struct MsgboxEventResult
{
Semaphore WaitForMe;
@ -84,7 +67,7 @@ protected:
wxString m_CustomLabel;
public:
MsgButtons() : bitset( 0 ) { }
MsgButtons() { bitset = 0; }
MsgButtons& OK() { m_OK = true; return *this; }
MsgButtons& Cancel() { m_Cancel = true; return *this; }
@ -253,6 +236,30 @@ protected:
};
// --------------------------------------------------------------------------------------
// pxStuckThreadEvent
// --------------------------------------------------------------------------------------
class pxStuckThreadEvent : public BaseMessageBoxEvent
{
typedef BaseMessageBoxEvent _parent;
DECLARE_DYNAMIC_CLASS_NO_ASSIGN( pxStuckThreadEvent )
protected:
Threading::PersistentThread& m_Thread;
public:
virtual ~pxStuckThreadEvent() throw() { }
virtual pxStuckThreadEvent *Clone() const { return new pxStuckThreadEvent(*this); }
pxStuckThreadEvent();
pxStuckThreadEvent( PersistentThread& thr );
pxStuckThreadEvent( MsgboxEventResult& instdata, PersistentThread& thr );
pxStuckThreadEvent( const pxStuckThreadEvent& src);
protected:
virtual int _DoDialog() const;
};
// --------------------------------------------------------------------------------------
// pxPingEvent
// --------------------------------------------------------------------------------------
@ -274,33 +281,56 @@ public:
Semaphore* GetSemaphore() { return m_PostBack; }
};
typedef void FnType_VoidMethod();
// --------------------------------------------------------------------------------------
// wxAppWithHelpers
// --------------------------------------------------------------------------------------
class wxAppWithHelpers : public wxApp
{
typedef wxApp _parent;
DECLARE_DYNAMIC_CLASS(wxAppWithHelpers)
protected:
std::vector<Semaphore*> m_PingWhenIdle;
wxTimer m_PingTimer;
std::vector<Semaphore*> m_PingWhenIdle;
std::vector<IDeletableObject*> m_DeleteWhenIdle;
Threading::Mutex m_DeleteIdleLock;
wxTimer m_PingTimer;
public:
wxAppWithHelpers();
virtual ~wxAppWithHelpers() {}
void CleanUp();
void DeleteObject( IDeletableObject& obj );
void DeleteObject( IDeletableObject* obj )
{
if( obj == NULL ) return;
DeleteObject( *obj );
}
void PostCommand( void* clientData, int evtType, int intParam=0, long longParam=0, const wxString& stringParam=wxEmptyString );
void PostCommand( int evtType, int intParam=0, long longParam=0, const wxString& stringParam=wxEmptyString );
void PostMethod( FnType_VoidMethod* method );
void Ping();
void PingDispatch( const char* action );
bool OnInit();
//int OnExit();
protected:
void PingDispatcher( const char* action );
void DeletionDispatcher();
void OnIdleEvent( wxIdleEvent& evt );
void OnPingEvent( pxPingEvent& evt );
void OnPingTimeout( wxTimerEvent& evt );
void OnMessageBox( pxMessageBoxEvent& evt );
void OnMessageBox( BaseMessageBoxEvent& evt );
void OnDeleteObject( wxCommandEvent& evt );
};
namespace Msgbox
{
extern int ShowModal( BaseMessageBoxEvent& evt );
}

View File

@ -19,5 +19,5 @@
#include <wx/event.h>
template class EventSource< wxCommandEvent >;
template class EventSource< int >;
//template class EventSource< wxCommandEvent >;
//template class EventSource< int >;

View File

@ -140,7 +140,7 @@ bool Threading::Mutex::TryAcquire()
// and messages *if* the lock is performed from the main GUI thread.
//
// Exceptions:
// ThreadTimedOut - See description of ThreadTimedOut for details
// ThreadDeadlock - See description of ThreadDeadlock for details
//
void Threading::Mutex::Acquire()
{
@ -152,7 +152,7 @@ void Threading::Mutex::Acquire()
else if( _WaitGui_RecursionGuard( "Mutex::Acquire" ) )
{
if( !AcquireWithoutYield(def_deadlock_timeout) )
throw Exception::ThreadTimedOut();
throw Exception::ThreadDeadlock();
}
else
{
@ -165,7 +165,7 @@ void Threading::Mutex::Acquire()
}
// Exceptions:
// ThreadTimedOut - See description of ThreadTimedOut for details
// ThreadDeadlock - See description of ThreadDeadlock for details
//
bool Threading::Mutex::Acquire( const wxTimeSpan& timeout )
{
@ -181,7 +181,7 @@ bool Threading::Mutex::Acquire( const wxTimeSpan& timeout )
if( timeout > def_deadlock_timeout )
{
if( AcquireWithoutYield(def_deadlock_timeout) ) return true;
throw Exception::ThreadTimedOut();
throw Exception::ThreadDeadlock();
}
return AcquireWithoutYield( timeout );
}
@ -200,7 +200,7 @@ bool Threading::Mutex::Acquire( const wxTimeSpan& timeout )
}
// Looks like a potential deadlock; throw an exception!
throw Exception::ThreadTimedOut();
throw Exception::ThreadDeadlock();
#else
return AcquireWithoutYield();
@ -215,7 +215,7 @@ bool Threading::Mutex::Acquire( const wxTimeSpan& timeout )
// Implemented internally as a simple Acquire/Release pair.
//
// Exceptions:
// ThreadTimedOut - See description of ThreadTimedOut for details
// ThreadDeadlock - See description of ThreadDeadlock for details
//
void Threading::Mutex::Wait()
{
@ -231,7 +231,7 @@ void Threading::Mutex::Wait()
// and the mutex is still locked by another thread.
//
// Exceptions:
// ThreadTimedOut - See description of ThreadTimedOut for details
// ThreadDeadlock - See description of ThreadDeadlock for details
//
bool Threading::Mutex::Wait( const wxTimeSpan& timeout )
{

View File

@ -80,7 +80,7 @@ bool Threading::Semaphore::WaitWithoutYield( const wxTimeSpan& timeout )
// called from another thread, no message pumping is performed.
//
// Exceptions:
// ThreadTimedOut - See description of ThreadTimedOut for details
// ThreadDeadlock - See description of ThreadDeadlock for details
//
void Threading::Semaphore::Wait()
{
@ -93,7 +93,7 @@ void Threading::Semaphore::Wait()
{
ScopedBusyCursor hourglass( Cursor_ReallyBusy );
if( !WaitWithoutYield(def_yieldgui_interval) ) // default is 4 seconds
throw Exception::ThreadTimedOut();
throw Exception::ThreadDeadlock();
}
else
{
@ -116,7 +116,7 @@ void Threading::Semaphore::Wait()
// reached prior to timeout.
//
// Exceptions:
// ThreadTimedOut - See description of ThreadTimedOut for details
// ThreadDeadlock - See description of ThreadDeadlock for details
//
bool Threading::Semaphore::Wait( const wxTimeSpan& timeout )
{
@ -131,7 +131,7 @@ bool Threading::Semaphore::Wait( const wxTimeSpan& timeout )
if( timeout > def_deadlock_timeout )
{
if( WaitWithoutYield(def_deadlock_timeout) ) return true;
throw Exception::ThreadTimedOut();
throw Exception::ThreadDeadlock();
}
return WaitWithoutYield( timeout );
}

View File

@ -24,12 +24,15 @@
# include <signal.h> // for pthread_kill, which is in pthread.h on w32-pthreads
#endif
#include "Threading.h"
#include "PersistentThread.h"
#include "wxBaseTools.h"
#include "ThreadingInternal.h"
#include "EventSource.inl"
using namespace Threading;
template class EventSource< EventListener_Thread >;
// 100ms interval for waitgui (issued from blocking semaphore waits on the main thread,
// to avoid gui deadlock).
const wxTimeSpan Threading::def_yieldgui_interval( 0, 0, 0, 100 );
@ -160,7 +163,7 @@ Threading::PersistentThread::~PersistentThread() throw()
Threading::Sleep( 1 );
Detach();
}
catch( Exception::ThreadTimedOut& ex )
catch( Exception::ThreadDeadlock& ex )
{
// Windows allows for a thread to be terminated forcefully, but it's not really
// a safe thing to do since typically threads are acquiring and releasing locks
@ -353,6 +356,12 @@ bool Threading::PersistentThread::IsRunning() const
return !!m_running;
}
void Threading::PersistentThread::AddListener( EventListener_Thread& evt )
{
evt.SetThread( this );
m_evtsrc_OnDelete.Add( evt );
}
// Throws an exception if the thread encountered one. Uses the BaseException's Rethrow() method,
// which ensures the exception type remains consistent. Debuggable stacktraces will be lost, since
// the thread will have allowed itself to terminate properly.

View File

@ -18,7 +18,16 @@
DEFINE_EVENT_TYPE( pxEvt_Ping );
DEFINE_EVENT_TYPE( pxEvt_MessageBox );
DEFINE_EVENT_TYPE( pxEvt_Assertion );
DEFINE_EVENT_TYPE( pxEvt_DeleteObject );
//DEFINE_EVENT_TYPE( pxEvt_Assertion );
void IDeletableObject::DoDeletion()
{
wxAppWithHelpers* app = wxDynamicCast( wxApp::GetInstance(), wxAppWithHelpers );
pxAssume( app != NULL );
app->DeleteObject( *this );
}
// --------------------------------------------------------------------------------------
// pxPingEvent Implementations
@ -44,6 +53,11 @@ pxPingEvent::pxPingEvent( const pxPingEvent& src )
m_PostBack = src.m_PostBack;
}
// --------------------------------------------------------------------------------------
// wxAppWithHelpers Implementation
// --------------------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS( wxAppWithHelpers, wxApp )
void wxAppWithHelpers::OnPingEvent( pxPingEvent& evt )
{
// Ping events are dispatched during the idle event handler, which ensures
@ -54,7 +68,13 @@ void wxAppWithHelpers::OnPingEvent( pxPingEvent& evt )
m_PingTimer.Start( 200, true );
}
void wxAppWithHelpers::PingDispatch( const char* action )
void wxAppWithHelpers::CleanUp()
{
DeletionDispatcher();
_parent::CleanUp();
}
void wxAppWithHelpers::PingDispatcher( const char* action )
{
size_t size = m_PingWhenIdle.size();
if( size == 0 ) return;
@ -69,16 +89,26 @@ void wxAppWithHelpers::PingDispatch( const char* action )
m_PingWhenIdle.clear();
}
void wxAppWithHelpers::DeletionDispatcher()
{
ScopedLock lock( m_DeleteIdleLock );
size_t size = m_DeleteWhenIdle.size();
if( size == 0 ) return;
DbgCon.WriteLn( Color_Gray, "App Idle Delete -> %u objects.", size );
}
void wxAppWithHelpers::OnIdleEvent( wxIdleEvent& evt )
{
evt.Skip();
m_PingTimer.Stop();
PingDispatch( "Idle" );
PingDispatcher( "Idle" );
}
void wxAppWithHelpers::OnPingTimeout( wxTimerEvent& evt )
{
PingDispatch( "Timeout" );
PingDispatcher( "Timeout" );
}
void wxAppWithHelpers::Ping()
@ -106,21 +136,29 @@ void wxAppWithHelpers::PostCommand( int evtType, int intParam, long longParam, c
PostCommand( NULL, evtType, intParam, longParam, stringParam );
}
void wxAppWithHelpers::DeleteObject( IDeletableObject& obj )
{
pxAssume( obj.IsBeingDeleted() );
ScopedLock lock( m_DeleteIdleLock );
m_DeleteWhenIdle.push_back( &obj );
}
typedef void (wxEvtHandler::*pxMessageBoxEventFunction)(pxMessageBoxEvent&);
typedef void (wxEvtHandler::*BaseMessageBoxEventFunction)(BaseMessageBoxEvent&);
typedef void (wxEvtHandler::*pxPingEventFunction)(pxPingEvent&);
bool wxAppWithHelpers::OnInit()
{
#define pxMessageBoxEventThing(func) \
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(pxMessageBoxEventFunction, &func )
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(BaseMessageBoxEventFunction, &func )
#define pxPingEventHandler(func) \
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(pxPingEventFunction, &func )
Connect( pxEvt_MessageBox, pxMessageBoxEventThing (wxAppWithHelpers::OnMessageBox) );
Connect( pxEvt_Assertion, pxMessageBoxEventThing (wxAppWithHelpers::OnMessageBox) );
//Connect( pxEvt_Assertion, pxMessageBoxEventThing (wxAppWithHelpers::OnMessageBox) );
Connect( pxEvt_Ping, pxPingEventHandler (wxAppWithHelpers::OnPingEvent) );
Connect( pxEvt_DeleteObject, wxCommandEventHandler (wxAppWithHelpers::OnDeleteObject) );
Connect( wxEVT_IDLE, wxIdleEventHandler (wxAppWithHelpers::OnIdleEvent) );
Connect( m_PingTimer.GetId(), wxEVT_TIMER, wxTimerEventHandler(wxAppWithHelpers::OnPingTimeout) );
@ -128,11 +166,17 @@ bool wxAppWithHelpers::OnInit()
return _parent::OnInit();
}
void wxAppWithHelpers::OnMessageBox( pxMessageBoxEvent& evt )
void wxAppWithHelpers::OnMessageBox( BaseMessageBoxEvent& evt )
{
evt.IssueDialog();
}
void wxAppWithHelpers::OnDeleteObject( wxCommandEvent& evt )
{
if( evt.GetClientData() == NULL ) return;
delete (IDeletableObject*)evt.GetClientData();
}
wxAppWithHelpers::wxAppWithHelpers()
: m_PingTimer( this )
{

View File

@ -17,6 +17,7 @@
#include "HashMap.h"
#include "wxGuiTools.h"
#include "pxStaticText.h"
#include "Threading.h"
#include <wx/cshelp.h>
#include <wx/tooltip.h>
@ -24,6 +25,43 @@
using namespace pxSizerFlags;
// --------------------------------------------------------------------------------------
// IDeletableObject Implementation
// --------------------------------------------------------------------------------------
// This code probably deserves a better home. It's general purpose non-GUI code (the single
// wxApp/Gui dependency is in wxGuiTools.cpp for now).
//
bool IDeletableObject::MarkForDeletion()
{
return !_InterlockedExchange( &m_IsBeingDeleted, true );
}
void IDeletableObject::DeleteSelf()
{
if( MarkForDeletion() )
DoDeletion();
}
IDeletableObject::IDeletableObject()
{
#ifdef _MSC_VER
// Bleh, this fails because _CrtIsValidHeapPointer calls HeapValidate on the
// pointer, but the pointer is a virtual base class, so it's not a valid block. >_<
//pxAssertDev( _CrtIsValidHeapPointer( this ), "IDeletableObject types cannot be created on the stack or as temporaries!" );
#endif
m_IsBeingDeleted = false;
}
IDeletableObject::~IDeletableObject() throw()
{
AffinityAssert_AllowFromMain();
}
// --------------------------------------------------------------------------------------
// Creates a text control which is right-justified and has it's minimum width configured to suit
// the number of digits requested.
wxTextCtrl* CreateNumericalTextCtrl( wxWindow* parent, int digits )

View File

@ -37,19 +37,6 @@ enum PluginsEnum_t
PluginId_Mcd
};
// This macro is actually useful for about any and every possible application of C++
// equality operators.
#define OpEqu( field ) (field == right.field)
// Macro used for removing some of the redtape involved in defining bitfield/union helpers.
//
#define BITFIELD32() \
union { \
u32 bitset; \
struct {
#define BITFIELD_END }; };
//------------ DEFAULT sseMXCSR VALUES ---------------
#define DEFAULT_sseMXCSR 0xffc0 //FPU rounding > DaZ, FtZ, "chop"
#define DEFAULT_sseVUMXCSR 0xffc0 //VU rounding > DaZ, FtZ, "chop"

View File

@ -292,7 +292,7 @@ protected:
// apps or DLLs to reference their own instance of SysMtgsThread (also allowing them
// to extend the class and override virtual methods).
//
SysMtgsThread& GetMTGS();
extern SysMtgsThread& GetMTGS();
/////////////////////////////////////////////////////////////////////////////
// Generalized GS Functions and Stuff

View File

@ -54,7 +54,7 @@ static __forceinline u8* iopPhysMem( u32 addr )
return &psxM[addr & 0x1fffff];
}
#define psxSs8(mem) psxS[(mem) & 0xffff]
#define psxSs8(mem) psxS[(mem) & 0x00ff]
#define psxSs16(mem) (*(s16*)&psxS[(mem) & 0x00ff])
#define psxSs32(mem) (*(s32*)&psxS[(mem) & 0x00ff])
#define psxSu8(mem) (*(u8*) &psxS[(mem) & 0x00ff])

View File

@ -30,12 +30,11 @@ static void SysPageFaultSignalFilter( int signal, siginfo_t *siginfo, void * )
// Note: Use of most stdio functions isn't safe here. Avoid console logs,
// assertions, file logs, or just about anything else useful.
PageFaultInfo pfinfo( (uptr)siginfo->si_addr & ~m_pagemask );
Source_PageFault.DispatchException( pfinfo );
Source_PageFault.Dispatch( PageFaultInfo( (uptr)siginfo->si_addr & ~m_pagemask ) );
// resumes execution right where we left off (re-executes instruction that
// caused the SIGSEGV).
if( pfinfo.handled ) return;
if( Source_PageFault.WasHandled() ) return;
// Bad mojo! Completely invalid address.
// Instigate a trap if we're in a debugger, and if not then do a SIGKILL.

View File

@ -565,7 +565,13 @@ void memClearPageAddr(u32 vaddr)
///////////////////////////////////////////////////////////////////////////
// PS2 Memory Init / Reset / Shutdown
static void __evt_fastcall mmap_OnPageFault( void* basemem, PageFaultInfo& info );
class mmap_PageFaultHandler : public IEventListener_PageFault
{
protected:
void OnPageFaultEvent( const PageFaultInfo& info, bool& handled );
};
mmap_PageFaultHandler mmap_faultHandler;
static const uint m_allMemSize =
Ps2MemSize::Rom + Ps2MemSize::Rom1 + Ps2MemSize::Rom2 + Ps2MemSize::ERom +
@ -590,12 +596,12 @@ void memAlloc()
psH = curpos; curpos += Ps2MemSize::Hardware;
psS = curpos; //curpos += Ps2MemSize::Scratch;
Source_PageFault.Add( EventListener<PageFaultInfo>((void*)psM, mmap_OnPageFault) );
Source_PageFault.Add( mmap_faultHandler );
}
void memShutdown()
{
Source_PageFault.Remove( EventListener<PageFaultInfo>((void*)psM, mmap_OnPageFault) );
Source_PageFault.Remove( mmap_faultHandler );
vtlb_free( m_psAllMem, m_allMemSize );
m_psAllMem = NULL;
@ -885,14 +891,14 @@ static __forceinline void mmap_ClearCpuBlock( uint offset )
Cpu->Clear( m_PageProtectInfo[rampage].ReverseRamMap, 0x400 );
}
static void __evt_fastcall mmap_OnPageFault( void* basemem, PageFaultInfo& info )
void mmap_PageFaultHandler::OnPageFaultEvent( const PageFaultInfo& info, bool& handled )
{
// get bad virtual address
uptr offset = info.addr - (uptr)basemem;
uptr offset = info.addr - (uptr)psM;
if( offset >= Ps2MemSize::Base ) return;
mmap_ClearCpuBlock( offset );
info.handled = true;
handled = true;
}
// Clears all block tracking statuses, manual protection flags, and write protection.

View File

@ -748,7 +748,7 @@ PluginManager::PluginManager( const wxString (&folders)[PluginId_Count] )
wxsFormat( L"Plugin Test failure, return code: %d", testres ),
_( "The plugin reports that your hardware or software/drivers are not supported." )
);
pxYield( 2 );
} while( ++pi, pi->shortname != NULL );

View File

@ -44,11 +44,20 @@ static bool StateCopy_ForceClear()
state_buffer.Dispose();
}
static void __evt_fastcall StateThread_OnAppStatus( void* thr, AppEventType& stat )
class EventListener_AppExiting : public IEventListener_AppStatus
{
if( (thr == NULL) || (stat != AppStatus_Exiting) ) return;
((PersistentThread*)thr)->Cancel();
}
protected:
PersistentThread& m_thread;
public:
EventListener_AppExiting( PersistentThread& thr )
: m_thread( thr )
{
}
virtual ~EventListener_AppExiting() throw() {}
};
enum
{
@ -59,17 +68,17 @@ enum
StateThreadAction_UnzipFromDisk,
};
class _BaseStateThread : public PersistentThread
class _BaseStateThread : public PersistentThread,
public virtual IEventListener_AppStatus,
public virtual IDeletableObject
{
typedef PersistentThread _parent;
protected:
EventListenerBinding<AppEventType> m_bind_OnExit;
bool m_isStarted;
// Holds the pause/suspend state of the emulator when the state load/stave chain of action is started,
// so that the proper state can be restoed automatically on completion.
// so that the proper state can be restored automatically on completion.
bool m_resume_when_done;
public:
@ -87,7 +96,6 @@ public:
protected:
_BaseStateThread( const char* name, FnType_OnThreadComplete* onFinished )
: m_bind_OnExit( wxGetApp().Source_AppStatus(), EventListener<AppEventType>( this, StateThread_OnAppStatus ) )
{
Callback_FreezeFinished = onFinished;
m_name = L"StateThread::" + fromUTF8(name);
@ -110,6 +118,14 @@ protected:
wxGetApp().PostCommand( this, pxEvt_FreezeThreadFinished, type, m_resume_when_done );
}
void AppStatusEvent_OnExit()
{
Cancel();
Pcsx2App& myapp( wxGetApp() );
myapp.RemoveListener( *this );
myapp.DeleteObject( *this );
}
};
// --------------------------------------------------------------------------------------

View File

@ -22,10 +22,34 @@
#include "System/PageFaultSource.h"
#include "Utilities/EventSource.inl"
template class EventSource< PageFaultInfo >;
template class EventSource< IEventListener_PageFault >;
SrcType_PageFault Source_PageFault;
IEventListener_PageFault::IEventListener_PageFault()
{
Source_PageFault.Add( *this );
}
IEventListener_PageFault::~IEventListener_PageFault() throw()
{
Source_PageFault.Remove( *this );
}
void SrcType_PageFault::Dispatch( const PageFaultInfo& params )
{
m_handled = false;
_parent::Dispatch( params );
}
void SrcType_PageFault::_DispatchRaw( ListenerIterator iter, const ListenerIterator& iend, const PageFaultInfo& evt )
{
do {
(*iter)->DispatchEvent( evt, m_handled );
} while( (++iter != iend) && !m_handled );
}
#if _MSC_VER
# include "svnrev.h"
#endif

View File

@ -107,10 +107,10 @@ extern void NTFS_CompressFile( const wxString& file, bool compressStatus=true );
namespace Msgbox
{
extern bool Alert( const wxString& text, const wxString& caption=L"PCSX2 Message", int icon=wxICON_EXCLAMATION );
extern bool OkCancel( const wxString& text, const wxString& caption=L"PCSX2 Message", int icon=0 );
extern bool YesNo( const wxString& text, const wxString& caption=L"PCSX2 Message", int icon=wxICON_QUESTION );
extern bool Alert( const wxString& text, const wxString& caption=L"PCSX2 Message", int icon=wxICON_EXCLAMATION );
extern bool OkCancel( const wxString& text, const wxString& caption=L"PCSX2 Message", int icon=0 );
extern bool YesNo( const wxString& text, const wxString& caption=L"PCSX2 Message", int icon=wxICON_QUESTION );
extern int Assertion( const wxString& text, const wxString& stacktrace );
extern int Assertion( const wxString& text, const wxString& stacktrace );
}

View File

@ -26,38 +26,58 @@
struct PageFaultInfo
{
uptr addr;
bool handled;
PageFaultInfo( uptr address )
{
addr = address;
handled = false;
}
};
class SrcType_PageFault : public EventSource<PageFaultInfo>
// --------------------------------------------------------------------------------------
// IEventListener_PageFault
// --------------------------------------------------------------------------------------
class IEventListener_PageFault : public IEventDispatcher<PageFaultInfo>
{
public:
typedef PageFaultInfo EvtParams;
public:
IEventListener_PageFault();
virtual ~IEventListener_PageFault() throw();
virtual void DispatchEvent( const PageFaultInfo& evtinfo, bool& handled )
{
OnPageFaultEvent( evtinfo, handled );
}
virtual void DispatchEvent( const PageFaultInfo& evtinfo )
{
pxFailRel( "Don't call me, damnit. Use DispatchException instead." );
}
protected:
virtual void OnPageFaultEvent( const PageFaultInfo& evtinfo, bool& handled ) {}
};
class SrcType_PageFault : public EventSource<IEventListener_PageFault>
{
protected:
typedef EventSource<IEventListener_PageFault> _parent;
protected:
bool m_handled;
public:
SrcType_PageFault() {}
virtual ~SrcType_PageFault() throw() { }
void DispatchException( PageFaultInfo& evt )
{
if( m_listeners.empty() ) return;
bool WasHandled() const { return m_handled; }
virtual void Dispatch( const PageFaultInfo& params );
ConstIterator iter( m_listeners.begin() );
const ConstIterator iend( m_listeners.end() );
do {
iter->OnEvent( iter->object, evt );
} while( (++iter != iend) && !evt.handled );
}
protected:
virtual void _DispatchRaw( ListenerIterator iter, const ListenerIterator& iend, const PageFaultInfo& evt );
};
extern SrcType_PageFault Source_PageFault;
extern void InstallSignalHandler();
#ifdef __LINUX__
# define PCSX2_PAGEFAULT_PROTECT
@ -65,8 +85,8 @@ extern void InstallSignalHandler();
#elif defined( _WIN32 )
struct _EXCEPTION_POINTERS;
extern int SysPageFaultExceptionFilter(struct _EXCEPTION_POINTERS* eps);
struct _EXCEPTION_POINTERS;
extern int SysPageFaultExceptionFilter(struct _EXCEPTION_POINTERS* eps);
# define PCSX2_PAGEFAULT_PROTECT __try
# define PCSX2_PAGEFAULT_EXCEPT __except(SysPageFaultExceptionFilter(GetExceptionInformation())) {}
@ -74,3 +94,8 @@ extern void InstallSignalHandler();
#else
# error PCSX2 - Unsupported operating system platform.
#endif
extern void InstallSignalHandler();
extern SrcType_PageFault Source_PageFault;

View File

@ -58,6 +58,27 @@ void SysThreadBase::OnStart()
_parent::OnStart();
}
// (overridable) Timeout period before a thread is considered potentially
// deadlocked. SysThreadBase default is 4 seconds.
//
wxTimeSpan SysThreadBase::GetDeadlockTimeout() const
{
return wxTimeSpan( 0, 0, 4, 0 );
}
void SysThreadBase::DoThreadDeadlocked()
{
}
void SysThreadBase::ThrowDeadlockException()
{
throw Exception::ThreadDeadlock( *this,
wxsFormat(L"Unhandled deadlock while suspending thread '%s'", m_name.c_str()),
wxsFormat(L"'%s' thread is not responding to suspend requests. It may be deadlocked or just running *really* slow.", m_name.c_str())
);
}
// Suspends emulation and closes the emulation state (including plugins) at the next PS2 vsync,
// and returns control to the calling thread; or does nothing if the core is already suspended.
//
@ -77,6 +98,9 @@ void SysThreadBase::OnStart()
// Suspension must cansel itself forcefully or risk crashing whatever other action is
// in progress.
//
// ThreadDeadlock - thrown if isBlocking is true and the thread to suspend fails to
// respond within the timeout period returned by GetDeadlockTimeout().
//
bool SysThreadBase::Suspend( bool isBlocking )
{
if( IsSelf() || !IsRunning() ) return false;
@ -112,15 +136,9 @@ bool SysThreadBase::Suspend( bool isBlocking )
if( isBlocking )
{
if( !m_RunningLock.Wait( wxTimeSpan( 0,0,3,0 ) ) )
if( !m_RunningLock.Wait( GetDeadlockTimeout() ) )
{
// [TODO] : Implement proper deadlock handler here that lets the user continue
// to wait, or issue a cancel to the thread.
throw Exception::ThreadTimedOut( *this,
wxsFormat(L"Possible deadlock while suspending thread '%s'", m_name.c_str()),
wxsFormat(L"'%s' thread is not responding to suspend requests. It may be deadlocked or just running *really* slow.", m_name.c_str())
);
DoThreadDeadlocked();
}
}
return retval;

View File

@ -15,7 +15,7 @@
#pragma once
#include "Utilities/Threading.h"
#include "Utilities/PersistentThread.h"
#include "x86emitter/tools.h"
using namespace Threading;
@ -123,7 +123,11 @@ public:
virtual bool AcquireResumeLock() { return m_ResumeProtection.TryAcquire(); }
virtual void ReleaseResumeLock() { m_ResumeProtection.Release(); }
virtual wxTimeSpan GetDeadlockTimeout() const;
virtual void ThrowDeadlockException();
protected:
virtual void DoThreadDeadlocked();
virtual void OnStart();
// This function is called by Resume immediately prior to releasing the suspension of

View File

@ -15,7 +15,7 @@
#pragma once
#include "wxAppWithHelpers.h"
#include "Utilities/wxAppWithHelpers.h"
#include <wx/fileconf.h>
#include <wx/imaglist.h>
@ -32,7 +32,7 @@
class Pcsx2App;
typedef void FnType_OnThreadComplete(const wxCommandEvent& evt);
typedef void (Pcsx2App::*FnType_AppMethod)();
typedef void (Pcsx2App::*FnPtr_AppMethod)();
BEGIN_DECLARE_EVENT_TYPES()
/*DECLARE_EVENT_TYPE( pxEVT_ReloadPlugins, -1 )
@ -43,9 +43,11 @@ BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE( pxEvt_LoadPluginsComplete, -1 )
DECLARE_EVENT_TYPE( pxEvt_PluginStatus, -1 )
DECLARE_EVENT_TYPE( pxEvt_SysExecute, -1 )
DECLARE_EVENT_TYPE( pxEvt_OpenModalDialog, -1 )
DECLARE_EVENT_TYPE( pxEvt_InvokeMethod, -1 )
DECLARE_EVENT_TYPE( pxEvt_LogicalVsync, -1 )
DECLARE_EVENT_TYPE( pxEvt_OpenModalDialog, -1 )
//DECLARE_EVENT_TYPE( pxEvt_StuckThread, -1 )
END_DECLARE_EVENT_TYPES()
// This is used when the GS plugin is handling its own window. Messages from the PAD
@ -156,6 +158,25 @@ enum MenuIdentifiers
MenuId_Config_ResetAll,
};
namespace Exception
{
// --------------------------------------------------------------------------
// Exception used to perform an "errorless" termination of the app during OnInit
// procedures. This happens when a user cancels out of startup prompts/wizards.
//
class StartupAborted : public BaseException
{
public:
DEFINE_EXCEPTION_COPYTORS( StartupAborted )
StartupAborted( const wxString& msg_eng=L"Startup initialization was aborted by the user." )
{
// english messages only for this exception.
BaseException::InitBaseEx( msg_eng, msg_eng );
}
};
}
// --------------------------------------------------------------------------------------
// KeyAcceleratorCode
@ -320,10 +341,12 @@ protected:
u64 m_fpsqueue_tally;
u64 m_ticks_lastframe;
int m_fpsqueue_writepos;
uint m_initpause;
uint m_FrameCounter;
public:
FramerateManager() { Reset(); }
virtual ~FramerateManager() throw() {}
void Reset();
@ -345,18 +368,89 @@ class Pcsx2App : public wxAppWithHelpers
// on them and they are, themselves, fairly self-contained.
protected:
EventSource<PluginEventType>m_evtsrc_CorePluginStatus;
CmdEvt_Source m_evtsrc_CoreThreadStatus;
EventSource<int> m_evtsrc_SettingsApplied;
EventSource<IniInterface> m_evtsrc_SettingsLoadSave;
EventSource<AppEventType> m_evtsrc_AppStatus;
EventSource<IEventListener_Plugins> m_evtsrc_CorePluginStatus;
EventSource<IEventListener_CoreThread> m_evtsrc_CoreThreadStatus;
EventSource<IEventListener_AppStatus> m_evtsrc_AppStatus;
public:
CmdEvt_Source& Source_CoreThreadStatus() { return m_evtsrc_CoreThreadStatus; }
EventSource<int>& Source_SettingsApplied() { return m_evtsrc_SettingsApplied; }
EventSource<AppEventType>& Source_AppStatus() { return m_evtsrc_AppStatus; }
EventSource<PluginEventType>& Source_CorePluginStatus() { return m_evtsrc_CorePluginStatus; }
EventSource<IniInterface>& Source_SettingsLoadSave() { return m_evtsrc_SettingsLoadSave; }
void AddListener( IEventListener_Plugins& listener )
{
m_evtsrc_CorePluginStatus.Add( listener );
}
void AddListener( IEventListener_CoreThread& listener )
{
m_evtsrc_CoreThreadStatus.Add( listener );
}
void AddListener( IEventListener_AppStatus& listener )
{
m_evtsrc_AppStatus.Add( listener );
}
void RemoveListener( IEventListener_Plugins& listener )
{
m_evtsrc_CorePluginStatus.Remove( listener );
}
void RemoveListener( IEventListener_CoreThread& listener )
{
m_evtsrc_CoreThreadStatus.Remove( listener );
}
void RemoveListener( IEventListener_AppStatus& listener )
{
m_evtsrc_AppStatus.Remove( listener );
}
void AddListener( IEventListener_Plugins* listener )
{
m_evtsrc_CorePluginStatus.Add( listener );
}
void AddListener( IEventListener_CoreThread* listener )
{
m_evtsrc_CoreThreadStatus.Add( listener );
}
void AddListener( IEventListener_AppStatus* listener )
{
m_evtsrc_AppStatus.Add( listener );
}
void RemoveListener( IEventListener_Plugins* listener )
{
m_evtsrc_CorePluginStatus.Remove( listener );
}
void RemoveListener( IEventListener_CoreThread* listener )
{
m_evtsrc_CoreThreadStatus.Remove( listener );
}
void RemoveListener( IEventListener_AppStatus* listener )
{
m_evtsrc_AppStatus.Remove( listener );
}
void DispatchEvent( PluginEventType evt )
{
if( !AffinityAssert_AllowFromMain() ) return;
m_evtsrc_CorePluginStatus.Dispatch( evt );
}
void DispatchEvent( AppEventType evt )
{
if( !AffinityAssert_AllowFromMain() ) return;
m_evtsrc_AppStatus.Dispatch( AppEventInfo( evt ) );
}
void DispatchEvent( IniInterface& ini )
{
if( !AffinityAssert_AllowFromMain() ) return;
m_evtsrc_AppStatus.Dispatch( AppSettingsEventInfo( ini ) );
}
// ----------------------------------------------------------------------------
public:
@ -374,13 +468,11 @@ public:
ScopedPtr<PluginManager> m_CorePlugins;
protected:
// Note: Pointers to frames should not be scoped because wxWidgets handles deletion
// of these objects internally.
MainEmuFrame* m_MainFrame;
GSFrame* m_gsFrame;
ConsoleLogFrame* m_ProgramLogBox;
wxWindowID m_id_MainFrame;
wxWindowID m_id_GsFrame;
wxWindowID m_id_ProgramLogBox;
wxKeyEvent m_kevt;
wxKeyEvent m_kevt;
public:
Pcsx2App();
@ -388,9 +480,9 @@ public:
void PostPluginStatus( PluginEventType pevt );
void PostMenuAction( MenuIdentifiers menu_id ) const;
int IssueModalDialog( const wxString& dlgName );
bool PrepForExit( bool canCancel );
int IssueDialogAsModal( const wxString& dlgName );
void PostMethod( FnPtr_AppMethod method );
bool DoStuckThread( PersistentThread& stuck_thread );
void SysExecute();
void SysExecute( CDVD_SourceType cdvdsrc, const wxString& elf_override=wxEmptyString );
@ -398,18 +490,33 @@ public:
void ReloadPlugins();
void LogicalVsync();
GSFrame& GetGSFrame() const;
GSFrame* GetGSFramePtr() const { return m_gsFrame; }
GSFrame& GetGsFrame() const;
MainEmuFrame& GetMainFrame() const;
MainEmuFrame* GetMainFramePtr() const { return m_MainFrame; }
bool HasMainFrame() const { return m_MainFrame != NULL; }
GSFrame* GetGsFramePtr() const { return (GSFrame*)wxWindow::FindWindowById( m_id_GsFrame ); }
MainEmuFrame* GetMainFramePtr() const { return (MainEmuFrame*)wxWindow::FindWindowById( m_id_MainFrame ); }
bool HasMainFrame() const { return GetMainFramePtr() != NULL; }
void OpenGsPanel();
void CloseGsPanel();
void OnGsFrameClosed();
void OnMainFrameClosed();
// --------------------------------------------------------------------------
// Startup / Shutdown Helpers
// --------------------------------------------------------------------------
void DetectCpuAndUserMode();
void OpenConsoleLog();
void OpenMainFrame();
bool PrepForExit( bool canCancel );
void CleanupRestartable();
void CleanupResources();
void WipeUserModeSettings();
void ReadUserModeSettings();
// --------------------------------------------------------------------------
// App-wide Resources
// --------------------------------------------------------------------------
@ -446,6 +553,7 @@ public:
// Console / Program Logging Helpers
// ----------------------------------------------------------------------------
ConsoleLogFrame* GetProgramLog();
const ConsoleLogFrame* GetProgramLog() const;
void ProgramLog_PostEvent( wxEvent& evt );
void EnableAllLogging() const;
void DisableWindowLogging() const;
@ -453,16 +561,17 @@ public:
void OnProgramLogClosed();
protected:
bool SelfMethodInvoke( FnType_AppMethod method );
bool SelfMethodPost( FnType_AppMethod method );
bool InvokeMethodOnMainThread( FnPtr_AppMethod method );
bool PostMethodToMainThread( FnPtr_AppMethod method );
void AllocateCoreStuffs();
void InitDefaultGlobalAccelerators();
void BuildCommandHash();
void ReadUserModeSettings();
bool TryOpenConfigCwd();
void CleanupMess();
void CleanupOnExit();
void OpenWizardConsole();
void PadKeyDispatch( const keyEvent& ev );
void CancelLoadingPlugins();
void HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent& event) const;
void HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent& event);
@ -470,10 +579,12 @@ protected:
void OnSysExecute( wxCommandEvent& evt );
void OnLoadPluginsComplete( wxCommandEvent& evt );
void OnPluginStatus( wxCommandEvent& evt );
void OnOpenModalDialog( wxCommandEvent& evt );
void OnCoreThreadStatus( wxCommandEvent& evt );
void OnFreezeThreadFinished( wxCommandEvent& evt );
void OnOpenModalDialog( wxCommandEvent& evt );
void OnOpenDialog_StuckThread( wxCommandEvent& evt );
void OnEmuKeyDown( wxKeyEvent& evt );
void OnInvokeMethod( pxInvokeMethodEvent& evt );
@ -497,17 +608,6 @@ protected:
// --------------------------------------------------------------------------------------
// AppCoreThread class
// --------------------------------------------------------------------------------------
enum CoreThreadStatus
{
CoreStatus_Indeterminate,
CoreStatus_Started,
CoreStatus_Resumed,
CoreStatus_Suspended,
CoreStatus_Reset,
CoreStatus_Stopped,
};
class AppCoreThread : public SysCoreThread
{
typedef SysCoreThread _parent;
@ -533,6 +633,8 @@ protected:
virtual void PostVsyncToUI();
virtual void ExecuteTaskInThread();
virtual void DoCpuReset();
virtual void DoThreadDeadlocked();
};
DECLARE_APP(Pcsx2App)
@ -568,7 +670,7 @@ DECLARE_APP(Pcsx2App)
if( MainEmuFrame* __frame_ = GetMainFramePtr() ) (*__frame_)
#define sGSFrame \
if( GSFrame* __gsframe_ = wxGetApp().GetGSFramePtr() ) (*__gsframe_)
if( GSFrame* __gsframe_ = wxGetApp().GetGsFramePtr() ) (*__gsframe_)
// Use this within the scope of a wxWindow (wxDialog or wxFrame). If the window has a valid menu
// bar, the command will run, otherwise it will be silently ignored. :)

View File

@ -17,7 +17,7 @@
#include "Utilities/SafeArray.h"
#include "Utilities/EventSource.h"
#include "Utilities/Threading.h"
#include "Utilities/PersistentThread.h"
#include "Utilities/wxGuiTools.h"
#include "Utilities/pxRadioPanel.h"
@ -25,37 +25,6 @@
#include "Utilities/pxStaticText.h"
#include "Utilities/CheckedStaticBox.h"
class MainEmuFrame;
class GSFrame;
class ConsoleLogFrame;
class PipeRedirectionBase;
class AppCoreThread;
class pxInvokeMethodEvent;
// wxWidgets forward declarations
class wxDirPickerCtrl;
class wxFileDirPickerEvent;
class wxListBox;
class wxListbook;
class wxBookCtrlBase;
enum AppEventType
{
// Maybe this will be expanded upon later..?
AppStatus_Exiting
};
enum PluginEventType
{
PluginsEvt_Loaded,
PluginsEvt_Init,
PluginsEvt_Opening, // dispatched prior to plugins being opened
PluginsEvt_Opened, // dispatched after plugins are opened
PluginsEvt_Closing, // dispatched prior to plugins being closed
PluginsEvt_Closed, // dispatched after plugins are closed
PluginsEvt_Shutdown,
PluginsEvt_Unloaded,
};
#include "AppForwardDefs.h"
#include "AppConfig.h"
#include "AppEventListeners.h"

View File

@ -683,16 +683,11 @@ void AppConfig_OnChangedSettingsFolder( bool overwrite )
throw Exception::AccessDenied( "Failed to overwrite settings; permission to file was denied." );
}
//if( GetAppConfig() != NULL )
// wxGetApp().Source_SettingsChanged().Dispatch( SettingsEvt_IniClosing );
// Bind into wxConfigBase to allow wx to use our config internally, and delete whatever
// comes out (cleans up prev config, if one).
delete wxConfigBase::Set( OpenFileConfig( iniFilename ) );
GetAppConfig()->SetRecordDefaults();
//wxGetApp().Source_SettingsChanged().Dispatch( SettingsEvt_IniOpening );
if( !overwrite )
AppLoadSettings();

View File

@ -15,13 +15,10 @@
#pragma once
#include "AppForwardDefs.h"
#include "PathDefs.h"
#include "CDVD/CDVDaccess.h"
class IniInterface;
class wxConfigBase;
class wxFileConfig;
extern bool UseAdminMode; // dictates if the program uses /home/user or /cwd for the program data
extern wxDirName SettingsFolder; // dictates where the settings folder comes from, *if* UseDefaultSettingsFolder is FALSE.
extern bool UseDefaultSettingsFolder; // when TRUE, pcsx2 derives the settings folder from the UseAdminMode

View File

@ -33,10 +33,10 @@ AppCoreThread::~AppCoreThread() throw()
void AppCoreThread::Cancel( bool isBlocking )
{
if( !_parent::Cancel( wxTimeSpan( 0,0,1,0 ) ) )
if( !_parent::Cancel( wxTimeSpan( 0, 0, 2, 0 ) ) )
{
// Possible deadlock!
throw Exception::ThreadTimedOut( this );
throw Exception::ThreadDeadlock( this );
}
}
@ -46,11 +46,18 @@ void AppCoreThread::Reset()
_parent::Reset();
}
void AppCoreThread::DoThreadDeadlocked()
{
//wxGetApp().PostCommand( );
wxGetApp().DoStuckThread( *this );
}
bool AppCoreThread::Suspend( bool isBlocking )
{
ScopedBusyCursor::SetDefault( Cursor_KindaBusy );
bool retval = _parent::Suspend( isBlocking );
bool retval = _parent::Suspend( false );
if( !retval || isBlocking )
ScopedBusyCursor::SetDefault( Cursor_NotBusy );

View File

@ -0,0 +1,197 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "Utilities/EventSource.h"
enum CoreThreadStatus
{
CoreStatus_Indeterminate,
CoreStatus_Started,
CoreStatus_Resumed,
CoreStatus_Suspended,
CoreStatus_Reset,
CoreStatus_Stopped,
};
enum AppEventType
{
AppStatus_SettingsLoaded,
AppStatus_SettingsSaved,
AppStatus_SettingsApplied,
AppStatus_Exiting
};
enum PluginEventType
{
PluginsEvt_Loaded,
PluginsEvt_Init,
PluginsEvt_Opening, // dispatched prior to plugins being opened
PluginsEvt_Opened, // dispatched after plugins are opened
PluginsEvt_Closing, // dispatched prior to plugins being closed
PluginsEvt_Closed, // dispatched after plugins are closed
PluginsEvt_Shutdown,
PluginsEvt_Unloaded,
};
struct AppEventInfo
{
AppEventType evt_type;
AppEventInfo( AppEventType type )
{
evt_type = type;
}
};
struct AppSettingsEventInfo : AppEventInfo
{
IniInterface& m_ini;
AppSettingsEventInfo( IniInterface& ini );
IniInterface& GetIni() const
{
return const_cast<IniInterface&>(m_ini);
}
};
// --------------------------------------------------------------------------------------
// IEventListener_CoreThread
// --------------------------------------------------------------------------------------
class IEventListener_CoreThread : public IEventDispatcher<CoreThreadStatus>
{
public:
typedef CoreThreadStatus EvtParams;
public:
IEventListener_CoreThread();
virtual ~IEventListener_CoreThread() throw();
virtual void DispatchEvent( const CoreThreadStatus& status )
{
switch( status )
{
case CoreStatus_Indeterminate: OnCoreStatus_Indeterminate(); break;
case CoreStatus_Started: OnCoreStatus_Started(); break;
case CoreStatus_Resumed: OnCoreStatus_Resumed(); break;
case CoreStatus_Suspended: OnCoreStatus_Suspended(); break;
case CoreStatus_Reset: OnCoreStatus_Reset(); break;
case CoreStatus_Stopped: OnCoreStatus_Stopped(); break;
}
}
protected:
virtual void OnCoreStatus_Indeterminate() {}
virtual void OnCoreStatus_Started() {}
virtual void OnCoreStatus_Resumed() {}
virtual void OnCoreStatus_Suspended() {}
virtual void OnCoreStatus_Reset() {}
virtual void OnCoreStatus_Stopped() {}
};
// --------------------------------------------------------------------------------------
// IEventListener_Plugins
// --------------------------------------------------------------------------------------
class IEventListener_Plugins : public IEventDispatcher<PluginEventType>
{
public:
typedef PluginEventType EvtParams;
public:
IEventListener_Plugins();
virtual ~IEventListener_Plugins() throw();
virtual void DispatchEvent( const PluginEventType& pevt )
{
switch( pevt )
{
case PluginsEvt_Loaded: OnPluginsEvt_Loaded(); break;
case PluginsEvt_Init: OnPluginsEvt_Init(); break;
case PluginsEvt_Opening: OnPluginsEvt_Opening(); break;
case PluginsEvt_Opened: OnPluginsEvt_Opened(); break;
case PluginsEvt_Closing: OnPluginsEvt_Closing(); break;
case PluginsEvt_Closed: OnPluginsEvt_Closed(); break;
case PluginsEvt_Shutdown: OnPluginsEvt_Shutdown(); break;
case PluginsEvt_Unloaded: OnPluginsEvt_Unloaded(); break;
}
}
protected:
virtual void OnPluginsEvt_Loaded() {}
virtual void OnPluginsEvt_Init() {}
virtual void OnPluginsEvt_Opening() {} // dispatched prior to plugins being opened
virtual void OnPluginsEvt_Opened() {} // dispatched after plugins are opened
virtual void OnPluginsEvt_Closing() {} // dispatched prior to plugins being closed
virtual void OnPluginsEvt_Closed() {} // dispatched after plugins are closed
virtual void OnPluginsEvt_Shutdown() {}
virtual void OnPluginsEvt_Unloaded() {}
};
// --------------------------------------------------------------------------------------
// IEventListener_AppStatus
// --------------------------------------------------------------------------------------
class IEventListener_AppStatus : public IEventDispatcher<AppEventInfo>
{
public:
typedef AppEventInfo EvtParams;
public:
IEventListener_AppStatus();
virtual ~IEventListener_AppStatus() throw();
virtual void DispatchEvent( const AppEventInfo& evtinfo );
protected:
virtual void AppStatusEvent_OnSettingsLoadSave( const AppSettingsEventInfo& evtinfo ) {}
virtual void AppStatusEvent_OnSettingsApplied() {}
virtual void AppStatusEvent_OnExit() {}
};
// --------------------------------------------------------------------------------------
// EventListenerHelper_AppStatus
// --------------------------------------------------------------------------------------
// Welcome to the awkward world of C++ multi-inheritence. wxWidgets' Connect() system is
// incompatible because of limitations in C++ class member function pointers, so we need
// this second layer class to act as a bridge between the event system and the class's
// handler implementations.
//
template< typename TypeToDispatchTo >
class EventListenerHelper_AppStatus : public IEventListener_AppStatus
{
public:
TypeToDispatchTo& Owner;
public:
EventListenerHelper_AppStatus( TypeToDispatchTo& dispatchTo )
: Owner( dispatchTo )
{
}
EventListenerHelper_AppStatus( TypeToDispatchTo* dispatchTo )
: Owner( *dispatchTo )
{
pxAssume(dispatchTo != NULL);
}
virtual ~EventListenerHelper_AppStatus() throw() {}
protected:
virtual void AppStatusEvent_OnSettingsLoadSave( const AppSettingsEventInfo& evtinfo ) { Owner.AppStatusEvent_OnSettingsLoadSave( evtinfo ); }
virtual void AppStatusEvent_OnSettingsApplied() { Owner.AppStatusEvent_OnSettingsApplied(); }
virtual void AppStatusEvent_OnExit() { Owner.AppStatusEvent_OnExit(); }
};

View File

@ -0,0 +1,73 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "App.h"
#include "IniInterface.h"
#include "Utilities/EventSource.inl"
template class EventSource< IEventListener_CoreThread >;
template class EventSource< IEventListener_Plugins >;
template class EventSource< IEventListener_AppStatus >;
AppSettingsEventInfo::AppSettingsEventInfo( IniInterface& ini )
: AppEventInfo( ini.IsSaving() ? AppStatus_SettingsSaved : AppStatus_SettingsLoaded )
, m_ini( ini )
{
}
IEventListener_CoreThread::IEventListener_CoreThread()
{
wxGetApp().AddListener( this );
}
IEventListener_CoreThread::~IEventListener_CoreThread() throw()
{
wxGetApp().RemoveListener( this );
}
IEventListener_Plugins::IEventListener_Plugins()
{
wxGetApp().AddListener( this );
}
IEventListener_Plugins::~IEventListener_Plugins() throw()
{
wxGetApp().RemoveListener( this );
}
IEventListener_AppStatus::IEventListener_AppStatus()
{
wxGetApp().AddListener( this );
}
IEventListener_AppStatus::~IEventListener_AppStatus() throw()
{
wxGetApp().RemoveListener( this );
}
void IEventListener_AppStatus::DispatchEvent( const AppEventInfo& evtinfo )
{
switch( evtinfo.evt_type )
{
case AppStatus_SettingsLoaded:
case AppStatus_SettingsSaved:
AppStatusEvent_OnSettingsLoadSave( (const AppSettingsEventInfo&)evtinfo );
break;
case AppStatus_SettingsApplied: AppStatusEvent_OnSettingsApplied(); break;
case AppStatus_Exiting: AppStatusEvent_OnExit(); break;
}
}

View File

@ -0,0 +1,44 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
// AppForwardDefs.h
//
// Purpose:
// This header file is meant to be a dependency-free include that provides a relatively
// full compliment of forward defines for PCSX2/App and wxwidgets types. When
// forward defined in this way, these types can be used by method and class definitions
// as either pointers or handles without running into complicated header file
// inter-dependence.
//
class MainEmuFrame;
class GSFrame;
class ConsoleLogFrame;
class PipeRedirectionBase;
class AppCoreThread;
class pxInvokeMethodEvent;
class IniInterface;
// wxWidgets forward declarations
class wxConfigBase;
class wxFileConfig;
class wxDirPickerCtrl;
class wxFileDirPickerEvent;
class wxListBox;
class wxListbook;
class wxBookCtrlBase;

View File

@ -28,25 +28,6 @@
static bool m_ForceWizard = false;
namespace Exception
{
// --------------------------------------------------------------------------
// Exception used to perform an "errorless" termination of the app during OnInit
// procedures. This happens when a user cancels out of startup prompts/wizards.
//
class StartupAborted : public BaseException
{
public:
DEFINE_EXCEPTION_COPYTORS( StartupAborted )
StartupAborted( const wxString& msg_eng=L"Startup initialization was aborted by the user." )
{
// english messages only for this exception.
BaseException::InitBaseEx( msg_eng, msg_eng );
}
};
}
static void CpuCheckSSE2()
{
if( x86caps.hasStreamingSIMD2Extensions ) return;
@ -77,10 +58,28 @@ void Pcsx2App::OpenWizardConsole()
{
if( !IsDebugBuild ) return;
g_Conf->ProgLogBox.Visible = true;
m_ProgramLogBox = new ConsoleLogFrame( NULL, L"PCSX2 Program Log", g_Conf->ProgLogBox );
m_id_ProgramLogBox = (new ConsoleLogFrame( NULL, L"PCSX2 Program Log", g_Conf->ProgLogBox ))->GetId();
EnableAllLogging();
}
void Pcsx2App::WipeUserModeSettings()
{
wxDirName usrlocaldir( wxStandardPaths::Get().GetUserLocalDataDir() );
if( !usrlocaldir.Exists() ) return;
wxString cwd( Path::Normalize( wxGetCwd() ) );
u32 hashres = HashTools::Hash( (char*)cwd.c_str(), cwd.Length() );
wxFileName usermodefile( FilenameDefs::GetUsermodeConfig() );
usermodefile.SetPath( usrlocaldir.ToString() );
ScopedPtr<wxFileConfig> conf_usermode( OpenFileConfig( usermodefile.GetFullPath() ) );
wxString groupname( wxsFormat( L"CWD.%08x", hashres ) );
Console.WriteLn( "(UserModeSettings) Removing entry:" );
Console.Indent().WriteLn( L"Path: %s\nHash:%s", cwd.c_str(), groupname.c_str() );
conf_usermode->DeleteGroup( groupname );
}
// User mode settings can't be stored in the CWD for two reasons:
// (a) the user may not have permission to do so (most obvious)
// (b) it would result in sloppy usermode.ini found all over a hard drive if people runs the
@ -91,9 +90,6 @@ void Pcsx2App::OpenWizardConsole()
//
void Pcsx2App::ReadUserModeSettings()
{
wxString cwd( Path::Normalize( wxGetCwd() ) );
u32 hashres = HashTools::Hash( (char*)cwd.c_str(), cwd.Length() );
wxDirName usrlocaldir( wxStandardPaths::Get().GetUserLocalDataDir() );
if( !usrlocaldir.Exists() )
{
@ -101,6 +97,9 @@ void Pcsx2App::ReadUserModeSettings()
usrlocaldir.Mkdir();
}
wxString cwd( Path::Normalize( wxGetCwd() ) );
u32 hashres = HashTools::Hash( (char*)cwd.c_str(), cwd.Length() );
wxFileName usermodefile( FilenameDefs::GetUsermodeConfig() );
usermodefile.SetPath( usrlocaldir.ToString() );
ScopedPtr<wxFileConfig> conf_usermode( OpenFileConfig( usermodefile.GetFullPath() ) );
@ -181,8 +180,146 @@ void Pcsx2App::ReadUserModeSettings()
// force a reset here to unload plugins loaded by the wizard. If we don't do this
// the recompilers might fail to allocate the memory they need to function.
SysReset();
sys_resume_lock = 0;
}
void Pcsx2App::DetectCpuAndUserMode()
{
cpudetectInit();
if( !x86caps.hasMultimediaExtensions )
{
// Note: due to memcpy_fast, we need minimum MMX even for interpreters. This will
// hopefully change later once we have a dynamically recompiled memcpy.
throw Exception::HardwareDeficiency(L"MMX Extensions not available.", _("PCSX2 requires cpu with MMX instruction to run."));
}
ReadUserModeSettings();
AppConfig_OnChangedSettingsFolder();
PostMethod( &Pcsx2App::OpenMainFrame );
PostMethod( &Pcsx2App::OpenConsoleLog );
PostMethod( &Pcsx2App::AllocateCoreStuffs );
}
void Pcsx2App::OpenMainFrame()
{
if( GetMainFramePtr() != NULL ) return;
MainEmuFrame* mainFrame = new MainEmuFrame( NULL, L"PCSX2" );
m_id_MainFrame = mainFrame->GetId();
mainFrame->PushEventHandler( &GetRecentIsoList() );
if( wxWindow* deleteme = GetProgramLog() )
{
deleteme->Destroy();
g_Conf->ProgLogBox.Visible = true;
PostMethod( &Pcsx2App::OpenConsoleLog );
}
SetTopWindow( mainFrame ); // not really needed...
SetExitOnFrameDelete( true ); // but being explicit doesn't hurt...
mainFrame->Show();
}
void Pcsx2App::OpenConsoleLog()
{
if( GetProgramLog() != NULL ) return;
m_id_ProgramLogBox = (new ConsoleLogFrame( GetMainFramePtr(), L"PCSX2 Program Log", g_Conf->ProgLogBox ))->GetId();
EnableAllLogging();
}
void Pcsx2App::AllocateCoreStuffs()
{
CpuCheckSSE2();
SysLogMachineCaps();
AppApplySettings();
if( m_CoreAllocs ) return;
m_CoreAllocs = new SysCoreAllocations();
if( m_CoreAllocs->HadSomeFailures( g_Conf->EmuOptions.Cpu.Recompiler ) )
{
// HadSomeFailures only returns 'true' if an *enabled* cpu type fails to init. If
// the user already has all interps configured, for example, then no point in
// popping up this dialog.
wxDialogWithHelpers exconf( NULL, _("PCSX2 Recompiler Error(s)"), wxVERTICAL );
exconf += 12;
exconf += exconf.Heading( pxE( ".Error:RecompilerInit",
L"Warning: Some of the configured PS2 recompilers failed to initialize and will not be available for this session:\n" )
);
wxTextCtrl* scrollableTextArea = new wxTextCtrl(
&exconf, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
wxTE_READONLY | wxTE_MULTILINE | wxTE_WORDWRAP
);
exconf += scrollableTextArea | pxSizerFlags::StdExpand();
if( !m_CoreAllocs->IsRecAvailable_EE() )
{
scrollableTextArea->AppendText( L"* R5900 (EE)\n\n" );
g_Conf->EmuOptions.Recompiler.EnableEE = false;
}
if( !m_CoreAllocs->IsRecAvailable_IOP() )
{
scrollableTextArea->AppendText( L"* R3000A (IOP)\n\n" );
g_Conf->EmuOptions.Recompiler.EnableIOP = false;
}
if( !m_CoreAllocs->IsRecAvailable_MicroVU0() )
{
scrollableTextArea->AppendText( L"* microVU0\n\n" );
g_Conf->EmuOptions.Recompiler.UseMicroVU0 = false;
g_Conf->EmuOptions.Recompiler.EnableVU0 = g_Conf->EmuOptions.Recompiler.EnableVU0 && m_CoreAllocs->IsRecAvailable_SuperVU0();
}
if( !m_CoreAllocs->IsRecAvailable_MicroVU1() )
{
scrollableTextArea->AppendText( L"* microVU1\n\n" );
g_Conf->EmuOptions.Recompiler.UseMicroVU1 = false;
g_Conf->EmuOptions.Recompiler.EnableVU1 = g_Conf->EmuOptions.Recompiler.EnableVU1 && m_CoreAllocs->IsRecAvailable_SuperVU1();
}
if( !m_CoreAllocs->IsRecAvailable_SuperVU0() )
{
scrollableTextArea->AppendText( L"* SuperVU0\n\n" );
g_Conf->EmuOptions.Recompiler.UseMicroVU0 = m_CoreAllocs->IsRecAvailable_MicroVU0();
g_Conf->EmuOptions.Recompiler.EnableVU0 = g_Conf->EmuOptions.Recompiler.EnableVU0 && g_Conf->EmuOptions.Recompiler.UseMicroVU0;
}
if( !m_CoreAllocs->IsRecAvailable_SuperVU1() )
{
scrollableTextArea->AppendText( L"* SuperVU1\n\n" );
g_Conf->EmuOptions.Recompiler.UseMicroVU1 = m_CoreAllocs->IsRecAvailable_MicroVU1();
g_Conf->EmuOptions.Recompiler.EnableVU1 = g_Conf->EmuOptions.Recompiler.EnableVU1 && g_Conf->EmuOptions.Recompiler.UseMicroVU1;
}
exconf += new ModalButtonPanel( &exconf, MsgButtons().OK() ) | pxSizerFlags::StdCenter();
exconf.ShowModal();
// Failures can be SSE-related OR memory related. Should do per-cpu error reports instead...
/*message += pxE( ".Popup Error:EmuCore:MemoryForRecs",
L"These errors are the result of memory allocation failures (see the program log for details). "
L"Closing out some memory hogging background tasks may resolve this error.\n\n"
L"These recompilers have been disabled and interpreters will be used in their place. "
L"Interpreters can be very slow, so don't get too excited. Press OK to continue or CANCEL to close PCSX2."
);*/
//if( !Msgbox::OkCancel( message, _("PCSX2 Initialization Error"), wxICON_ERROR ) )
// return false;
}
LoadPluginsPassive( NULL );
}
void Pcsx2App::OnInitCmdLine( wxCmdLineParser& parser )
{
parser.SetLogo( (wxString)L" >> PCSX2 -- A Playstation2 Emulator for the PC <<\n\n" +
@ -261,14 +398,14 @@ bool Pcsx2App::OnCmdLineParsed( wxCmdLineParser& parser )
}
typedef void (wxEvtHandler::*pxInvokeMethodEventFunction)(pxInvokeMethodEvent&);
typedef void (wxEvtHandler::*pxStuckThreadEventHandler)(pxMessageBoxEvent&);
// ------------------------------------------------------------------------
bool Pcsx2App::OnInit()
{
#define pxMethodEventHandler(func) \
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(pxInvokeMethodEventFunction, &func )
Connect( pxEvt_OpenModalDialog, wxCommandEventHandler( Pcsx2App::OnOpenModalDialog ) );
Connect( pxEvt_OpenModalDialog, wxCommandEventHandler( Pcsx2App::OnOpenModalDialog ) );
pxDoAssert = AppDoAssert;
@ -282,11 +419,6 @@ bool Pcsx2App::OnInit()
m_StderrRedirHandle = NewPipeRedir(stderr);
wxLocale::AddCatalogLookupPathPrefix( wxGetCwd() );
/*Connect( pxEVT_ReloadPlugins, wxCommandEventHandler (Pcsx2App::OnReloadPlugins) );
Connect( pxEVT_LogicalVsync, wxCommandEventHandler (Pcsx2App::OnLogicalVsync) );
Connect( pxEVT_OpenGsPanel, wxCommandEventHandler (Pcsx2App::OpenGsPanel) );*/
Connect( pxEvt_FreezeThreadFinished, wxCommandEventHandler (Pcsx2App::OnFreezeThreadFinished) );
Connect( pxEvt_CoreThreadStatus, wxCommandEventHandler (Pcsx2App::OnCoreThreadStatus) );
Connect( pxEvt_LoadPluginsComplete, wxCommandEventHandler (Pcsx2App::OnLoadPluginsComplete) );
@ -314,135 +446,23 @@ bool Pcsx2App::OnInit()
m_Resources = new pxAppResources();
cpudetectInit();
if( !x86caps.hasMultimediaExtensions )
{
// Note: due to memcpy_fast, we need minimum MMX even for interpreters. This will
// hopefully change later once we have a dynamically recompiled memcpy.
Msgbox::Alert( _("PCSX2 requires cpu with MMX instruction to run. Press OK to close."), _("PCSX2 - MMX Required") );
return false;
}
ReadUserModeSettings();
AppConfig_OnChangedSettingsFolder();
CpuCheckSSE2();
m_MainFrame = new MainEmuFrame( NULL, L"PCSX2" );
m_MainFrame->PushEventHandler( &GetRecentIsoList() );
if( m_ProgramLogBox )
{
wxWindow* deleteme = m_ProgramLogBox;
OnProgramLogClosed();
delete deleteme;
g_Conf->ProgLogBox.Visible = true;
}
m_ProgramLogBox = new ConsoleLogFrame( m_MainFrame, L"PCSX2 Program Log", g_Conf->ProgLogBox );
EnableAllLogging();
SetTopWindow( m_MainFrame ); // not really needed...
SetExitOnFrameDelete( true ); // but being explicit doesn't hurt...
m_MainFrame->Show();
SysLogMachineCaps();
AppApplySettings();
#ifdef __WXMSW__
pxDwm_Load();
#endif
m_CoreAllocs = new SysCoreAllocations();
if( m_CoreAllocs->HadSomeFailures( g_Conf->EmuOptions.Cpu.Recompiler ) )
{
// HadSomeFailures only returns 'true' if an *enabled* cpu type fails to init. If
// the user already has all interps configured, for example, then no point in
// popping up this dialog.
wxDialogWithHelpers exconf( NULL, _("PCSX2 Recompiler Error(s)"), wxVERTICAL );
exconf += 12;
exconf += exconf.Heading( pxE( ".Error:RecompilerInit",
L"Warning: Some of the configured PS2 recompilers failed to initialize and will not be available for this session:\n" )
);
wxTextCtrl* scrollableTextArea = new wxTextCtrl(
&exconf, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
wxTE_READONLY | wxTE_MULTILINE | wxTE_WORDWRAP
);
exconf += scrollableTextArea | pxSizerFlags::StdExpand();
if( !m_CoreAllocs->IsRecAvailable_EE() )
{
scrollableTextArea->AppendText( L"* R5900 (EE)\n\n" );
g_Conf->EmuOptions.Recompiler.EnableEE = false;
}
if( !m_CoreAllocs->IsRecAvailable_IOP() )
{
scrollableTextArea->AppendText( L"* R3000A (IOP)\n\n" );
g_Conf->EmuOptions.Recompiler.EnableIOP = false;
}
if( !m_CoreAllocs->IsRecAvailable_MicroVU0() )
{
scrollableTextArea->AppendText( L"* microVU0\n\n" );
g_Conf->EmuOptions.Recompiler.UseMicroVU0 = false;
g_Conf->EmuOptions.Recompiler.EnableVU0 = g_Conf->EmuOptions.Recompiler.EnableVU0 && m_CoreAllocs->IsRecAvailable_SuperVU0();
}
if( !m_CoreAllocs->IsRecAvailable_MicroVU1() )
{
scrollableTextArea->AppendText( L"* microVU1\n\n" );
g_Conf->EmuOptions.Recompiler.UseMicroVU1 = false;
g_Conf->EmuOptions.Recompiler.EnableVU1 = g_Conf->EmuOptions.Recompiler.EnableVU1 && m_CoreAllocs->IsRecAvailable_SuperVU1();
}
if( !m_CoreAllocs->IsRecAvailable_SuperVU0() )
{
scrollableTextArea->AppendText( L"* SuperVU0\n\n" );
g_Conf->EmuOptions.Recompiler.UseMicroVU0 = m_CoreAllocs->IsRecAvailable_MicroVU0();
g_Conf->EmuOptions.Recompiler.EnableVU0 = g_Conf->EmuOptions.Recompiler.EnableVU0 && g_Conf->EmuOptions.Recompiler.UseMicroVU0;
}
if( !m_CoreAllocs->IsRecAvailable_SuperVU1() )
{
scrollableTextArea->AppendText( L"* SuperVU1\n\n" );
g_Conf->EmuOptions.Recompiler.UseMicroVU1 = m_CoreAllocs->IsRecAvailable_MicroVU1();
g_Conf->EmuOptions.Recompiler.EnableVU1 = g_Conf->EmuOptions.Recompiler.EnableVU1 && g_Conf->EmuOptions.Recompiler.UseMicroVU1;
}
exconf += new ModalButtonPanel( &exconf, MsgButtons().OK() ) | pxSizerFlags::StdCenter();
exconf.ShowModal();
// Failures can be SSE-related OR memory related. Should do per-cpu error reports instead...
/*message += pxE( ".Popup Error:EmuCore:MemoryForRecs",
L"These errors are the result of memory allocation failures (see the program log for details). "
L"Closing out some memory hogging background tasks may resolve this error.\n\n"
L"These recompilers have been disabled and interpreters will be used in their place. "
L"Interpreters can be very slow, so don't get too excited. Press OK to continue or CANCEL to close PCSX2."
);*/
//if( !Msgbox::OkCancel( message, _("PCSX2 Initialization Error"), wxICON_ERROR ) )
// return false;
}
LoadPluginsPassive( NULL );
DetectCpuAndUserMode();
}
// ----------------------------------------------------------------------------
catch( Exception::StartupAborted& ex )
catch( Exception::StartupAborted& ex ) // user-aborted, no popups needed.
{
Console.Warning( ex.FormatDiagnosticMessage() );
CleanupMess();
CleanupOnExit();
return false;
}
catch( Exception::HardwareDeficiency& ex )
{
Msgbox::Alert( ex.FormatDisplayMessage() + _("\n\nPress OK to close PCSX2."), _("PCSX2 Error: Hardware Deficiency") );
CleanupOnExit();
return false;
}
// ----------------------------------------------------------------------------
@ -453,29 +473,52 @@ bool Pcsx2App::OnInit()
catch( Exception::RuntimeError& ex )
{
Console.Error( ex.FormatDiagnosticMessage() );
Msgbox::Alert( ex.FormatDisplayMessage() + L"\n\nPress OK to close PCSX2.",
Msgbox::Alert( ex.FormatDisplayMessage() + _("\n\nPress OK to close PCSX2."),
_("PCSX2 Critical Error"), wxICON_ERROR );
CleanupOnExit();
return false;
}
return true;
}
void Pcsx2App::CleanupMess()
// This cleanup procedure can only be called when the App message pump is still active.
// OnExit() must use CleanupOnExit instead.
void Pcsx2App::CleanupRestartable()
{
// app is shutting down, so don't let the system resume for anything. (sometimes there
// are pending Resume messages in the queue from previous user actions)
AffinityAssert_AllowFromMain();
// app is shutting down, so don't let the system resume for anything. (sometimes
// there are pending Resume messages in the queue from previous user actions, and
// this will block them from executing).
sys_resume_lock += 10;
PingDispatcher( "Cleanup" );
DeletionDispatcher();
CoreThread.Cancel();
if( m_CorePlugins )
m_CorePlugins->Shutdown();
if( g_Conf )
AppSaveSettings();
sMainFrame.RemoveEventHandler( &GetRecentIsoList() );
}
// This cleanup handler can be called from OnExit (it doesn't need a running message pump),
// but should not be called from the App destructor. It's needed because wxWidgets doesn't
// always call OnExit(), so I had to make CleanupRestartable, and then encapsulate it here
// to be friendly to the OnExit scenario (no message pump).
void Pcsx2App::CleanupOnExit()
{
AffinityAssert_AllowFromMain();
try
{
sys_resume_lock += 10;
PingDispatch( "Cleanup" );
CoreThread.Cancel();
if( m_CorePlugins )
m_CorePlugins->Shutdown();
CleanupRestartable();
}
catch( Exception::ThreadTimedOut& ) { throw; }
catch( Exception::ThreadDeadlock& ) { throw; }
catch( Exception::CancelEvent& ) { throw; }
catch( Exception::RuntimeError& ex )
{
@ -483,7 +526,7 @@ void Pcsx2App::CleanupMess()
// that we just don't care about by now, and just want to "get 'er done!" so
// we can exit the app. ;)
Console.Error( L"Runtime exception handled during CleanupMess:\n" );
Console.Error( L"Runtime exception handled during CleanupOnExit:\n" );
Console.Indent().Error( ex.FormatDiagnosticMessage() );
}
@ -499,17 +542,33 @@ void Pcsx2App::CleanupMess()
// FIXME: performing a wxYield() here may fix that problem. -- air
pxDoAssert = pxAssertImpl_LogIt;
Console_SetActiveHandler( ConsoleWriter_Stdout );
}
void Pcsx2App::CleanupResources()
{
delete wxConfigBase::Set( NULL );
while( wxGetLocale() != NULL )
delete wxGetLocale();
pxDoAssert = pxAssertImpl_LogIt;
}
int Pcsx2App::OnExit()
{
CleanupOnExit();
CleanupResources();
m_Resources = NULL;
return wxApp::OnExit();
}
Pcsx2App::Pcsx2App()
{
m_MainFrame = NULL;
m_gsFrame = NULL;
m_ProgramLogBox = NULL;
m_id_MainFrame = wxID_ANY;
m_id_GsFrame = wxID_ANY;
m_id_ProgramLogBox = wxID_ANY;
SetAppName( L"pcsx2" );
BuildCommandHash();
@ -519,16 +578,10 @@ Pcsx2App::~Pcsx2App()
{
pxDoAssert = pxAssertImpl_LogIt;
// Typically OnExit cleans everything up before we get here, *unless* we cancel
// out of program startup in OnInit (return false) -- then remaining cleanup needs
// to happen here in the destructor.
CleanupResources();
m_Resources = NULL;
DisableDiskLogging();
CleanupMess();
delete wxConfigBase::Set( NULL );
Console_SetActiveHandler( ConsoleWriter_Null );
if( emuLog != NULL )
{
fclose( emuLog );

View File

@ -32,22 +32,16 @@
IMPLEMENT_APP(Pcsx2App)
/*DEFINE_EVENT_TYPE( pxEVT_ReloadPlugins );
DEFINE_EVENT_TYPE( pxEVT_OpenGsPanel );*/
DEFINE_EVENT_TYPE( pxEvt_FreezeThreadFinished );
DEFINE_EVENT_TYPE( pxEvt_CoreThreadStatus );
DEFINE_EVENT_TYPE( pxEvt_LoadPluginsComplete );
DEFINE_EVENT_TYPE( pxEvt_PluginStatus );
DEFINE_EVENT_TYPE( pxEvt_SysExecute );
DEFINE_EVENT_TYPE( pxEvt_OpenModalDialog );
DEFINE_EVENT_TYPE( pxEvt_InvokeMethod );
DEFINE_EVENT_TYPE( pxEvt_LogicalVsync );
#include "Utilities/EventSource.inl"
template class EventSource< IniInterface >;
template class EventSource< AppEventType >;
template class EventSource< PluginEventType >;
DEFINE_EVENT_TYPE( pxEvt_OpenModalDialog );
//DEFINE_EVENT_TYPE( pxEvt_OpenDialog_StuckThread );
bool UseAdminMode = false;
wxDirName SettingsFolder;
@ -69,7 +63,7 @@ static bool HandlePluginError( Exception::PluginError& ex )
g_Conf->SysSettingsTabName = L"Plugins";
// fixme: Send a message to the panel to select the failed plugin.
if( wxGetApp().IssueModalDialog( Dialogs::SysConfigDialog::GetNameStatic() ) == wxID_CANCEL )
if( wxGetApp().IssueDialogAsModal( Dialogs::SysConfigDialog::GetNameStatic() ) == wxID_CANCEL )
return false;
}
return result;
@ -79,13 +73,14 @@ static bool HandlePluginError( Exception::PluginError& ex )
// And it's Thread Safe!
void Pcsx2App::PostMenuAction( MenuIdentifiers menu_id ) const
{
if( m_MainFrame == NULL ) return;
MainEmuFrame* mainFrame = GetMainFramePtr();
if( mainFrame == NULL ) return;
wxCommandEvent joe( wxEVT_COMMAND_MENU_SELECTED, menu_id );
if( wxThread::IsMain() )
m_MainFrame->GetEventHandler()->ProcessEvent( joe );
mainFrame->GetEventHandler()->ProcessEvent( joe );
else
m_MainFrame->GetEventHandler()->AddPendingEvent( joe );
mainFrame->GetEventHandler()->AddPendingEvent( joe );
}
// --------------------------------------------------------------------------------------
@ -100,25 +95,25 @@ class pxInvokeMethodEvent : public pxPingEvent
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxInvokeMethodEvent)
protected:
FnType_AppMethod m_Method;
FnPtr_AppMethod m_Method;
public:
virtual ~pxInvokeMethodEvent() throw() { }
virtual pxInvokeMethodEvent *Clone() const { return new pxInvokeMethodEvent(*this); }
explicit pxInvokeMethodEvent( int msgtype, FnType_AppMethod method=NULL, Semaphore* sema=NULL )
explicit pxInvokeMethodEvent( int msgtype, FnPtr_AppMethod method=NULL, Semaphore* sema=NULL )
: pxPingEvent( msgtype, sema )
{
m_Method = method;
}
explicit pxInvokeMethodEvent( FnType_AppMethod method=NULL, Semaphore* sema=NULL )
explicit pxInvokeMethodEvent( FnPtr_AppMethod method=NULL, Semaphore* sema=NULL )
: pxPingEvent( pxEvt_InvokeMethod, sema )
{
m_Method = method;
}
explicit pxInvokeMethodEvent( FnType_AppMethod method, Semaphore& sema )
explicit pxInvokeMethodEvent( FnPtr_AppMethod method, Semaphore& sema )
: pxPingEvent( pxEvt_InvokeMethod, &sema )
{
m_Method = method;
@ -136,7 +131,7 @@ public:
if( m_PostBack ) m_PostBack->Post();
}
void SetMethod( FnType_AppMethod method )
void SetMethod( FnPtr_AppMethod method )
{
m_Method = method;
}
@ -182,7 +177,9 @@ void Pcsx2App::PadKeyDispatch( const keyEvent& ev )
// GS window while the PAD plugin is open, so send messages to the APP handler
// only if *either* the GS or PAD plugins are in legacy mode.
if( m_gsFrame == NULL || (PADopen != NULL) )
GSFrame* gsFrame = wxGetApp().GetGsFramePtr();
if( gsFrame == NULL || (PADopen != NULL) )
{
if( m_kevt.GetEventType() == wxEVT_KEY_DOWN )
{
@ -192,14 +189,15 @@ void Pcsx2App::PadKeyDispatch( const keyEvent& ev )
}
else
{
m_kevt.SetId( m_gsFrame->GetViewport()->GetId() );
m_gsFrame->ProcessEvent( m_kevt );
m_kevt.SetId( gsFrame->GetViewport()->GetId() );
gsFrame->ProcessEvent( m_kevt );
}
}
void FramerateManager::Reset()
{
memzero( m_fpsqueue );
m_initpause = FramerateQueueDepth;
m_fpsqueue_tally = 0;
m_fpsqueue_writepos = 0;
Resume();
@ -224,11 +222,13 @@ void FramerateManager::DoFrame()
m_fpsqueue[m_fpsqueue_writepos] = elapsed_time;
m_fpsqueue_writepos = (m_fpsqueue_writepos + 1) % FramerateQueueDepth;
if( m_initpause > 0 ) --m_initpause;
}
double FramerateManager::GetFramerate() const
{
u32 ticks_per_frame = m_fpsqueue_tally / FramerateQueueDepth;
if( m_initpause > (FramerateQueueDepth/2) ) return 0.0;
u32 ticks_per_frame = m_fpsqueue_tally / (FramerateQueueDepth-m_initpause);
return (double)GetTickFrequency() / (double)ticks_per_frame;
}
@ -237,7 +237,7 @@ double FramerateManager::GetFramerate() const
// times a second if not (ok, not quite, but you get the idea... I hope.)
void Pcsx2App::LogicalVsync()
{
if( SelfMethodPost( &Pcsx2App::LogicalVsync ) ) return;
if( PostMethodToMainThread( &Pcsx2App::LogicalVsync ) ) return;
if( !SysHasValidState() || g_plugins == NULL ) return;
@ -247,8 +247,8 @@ void Pcsx2App::LogicalVsync()
// Only call PADupdate here if we're using GSopen2. Legacy GSopen plugins have the
// GS window belonging to the MTGS thread.
if( (PADupdate != NULL) && (GSopen2 != NULL) && (m_gsFrame != NULL) )
PADupdate(0);
if( (PADupdate != NULL) && (GSopen2 != NULL) && (wxGetApp().GetGsFramePtr() != NULL) )
PADupdate(0);
const keyEvent* ev = PADkeyEvent();
@ -295,7 +295,7 @@ void Pcsx2App::OnCoreThreadStatus( wxCommandEvent& evt )
m_kevt.m_controlDown = false;
m_kevt.m_altDown = false;
m_evtsrc_CoreThreadStatus.Dispatch( evt );
m_evtsrc_CoreThreadStatus.Dispatch( status );
ScopedBusyCursor::SetDefault( Cursor_NotBusy );
CoreThread.RethrowException();
}
@ -306,7 +306,7 @@ void Pcsx2App::OnOpenModalDialog( wxCommandEvent& evt )
MsgboxEventResult* evtres = (MsgboxEventResult*)evt.GetClientData();
wxWindowID result = IssueModalDialog( evt.GetString() );
wxWindowID result = IssueDialogAsModal( evt.GetString() );
if( evtres != NULL )
{
@ -315,7 +315,34 @@ void Pcsx2App::OnOpenModalDialog( wxCommandEvent& evt )
}
}
int Pcsx2App::IssueModalDialog( const wxString& dlgName )
void Pcsx2App::OnOpenDialog_StuckThread( wxCommandEvent& evt )
{
if( !pxAssert( evt.GetClientData() != NULL ) ) return;
DoStuckThread( *(PersistentThread*)evt.GetClientData() );
}
bool Pcsx2App::DoStuckThread( PersistentThread& stuck_thread )
{
if( !wxThread::IsMain() )
{
//PostCommand( &stuck_thread, pxEvt_OpenDialog_StuckThread );
}
// Parent the dialog to the GS window if it belongs to PCSX2. If not
// we should bind it to the Main window, and if that's not around, use NULL.
wxWindow* parent = GetGsFramePtr();
if( parent == NULL )
parent = GetMainFramePtr();
pxStuckThreadEvent evt( stuck_thread );
return Msgbox::ShowModal( evt );
}
// Opens the specified standard dialog as a modal dialog, or forces the an existing
// instance of the dialog (ie, it's already open) to be modal. This is needed for
// items which are
int Pcsx2App::IssueDialogAsModal( const wxString& dlgName )
{
if( dlgName.IsEmpty() ) return wxID_CANCEL;
@ -409,6 +436,12 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
(handler->*func)(event);
}
// ----------------------------------------------------------------------------
catch( Exception::StartupAborted& ex ) // user-aborted, no popups needed.
{
Console.Warning( ex.FormatDiagnosticMessage() );
Exit();
}
// ----------------------------------------------------------------------------
catch( Exception::BiosLoadFailed& ex )
{
wxDialogWithHelpers dialog( NULL, _("PS2 BIOS Error"), wxVERTICAL );
@ -418,7 +451,7 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
if( dialog.ShowModal() == wxID_CANCEL )
Console.Warning( "User denied option to re-configure BIOS." );
if( IssueModalDialog( Dialogs::BiosSelectorDialog::GetNameStatic() ) != wxID_CANCEL )
if( IssueDialogAsModal( Dialogs::BiosSelectorDialog::GetNameStatic() ) != wxID_CANCEL )
{
SysExecute();
}
@ -455,7 +488,7 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
}
}
// ----------------------------------------------------------------------------
catch( Exception::ThreadTimedOut& ex )
catch( Exception::ThreadDeadlock& ex )
{
// [TODO] Bind a listener to the CoreThread status, and automatically close the dialog
// if the thread starts responding while we're waiting (not hard in fact, but I'm getting
@ -505,14 +538,19 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
}
}
static void __evt_fastcall OnStateSaveFinished( void* obj, wxCommandEvent& evt )
class CancelCoreThreadWhenSaveStateDone : public IEventListener_CoreThread,
public IDeletableObject
{
if( evt.GetInt() == CoreStatus_Resumed )
public:
virtual ~CancelCoreThreadWhenSaveStateDone() throw() {}
void OnCoreStatus_Resumed()
{
wxGetApp().PostMenuAction( MenuId_Exit );
wxGetApp().Source_CoreThreadStatus().Remove( NULL, OnStateSaveFinished );
Pcsx2App& myapp( wxGetApp() );
myapp.DeleteObject( this );
myapp.PostMenuAction( MenuId_Exit );
}
}
};
// Common exit handler which can be called from any event (though really it should
// be called only from CloseWindow handlers since that's the more appropriate way
@ -524,13 +562,15 @@ bool Pcsx2App::PrepForExit( bool canCancel )
{
// If a savestate is saving, we should wait until it finishes. Otherwise the user
// might lose data.
if( StateCopy_IsBusy() )
{
Source_CoreThreadStatus().Add( NULL, OnStateSaveFinished );
throw Exception::CancelEvent( "Savestate in progress, cannot close program (close event delayed)" );
new CancelCoreThreadWhenSaveStateDone();
throw Exception::CancelEvent( "Savestate in progress, close event delayed until action is complete." );
}
CancelLoadingPlugins();
/*
if( canCancel )
{
@ -548,27 +588,16 @@ bool Pcsx2App::PrepForExit( bool canCancel )
}
}*/
AppEventType toSend = AppStatus_Exiting;
m_evtsrc_AppStatus.Dispatch( toSend );
CleanupMess();
DispatchEvent( AppStatus_Exiting );
// This should be called by OnExit(), but sometimes wxWidgets fails to call OnExit(), so
// do it here just in case (no harm anyway -- OnExit is the next logical step after
// CloseWindow returns true from the TopLevel window).
CleanupRestartable();
return true;
}
int Pcsx2App::OnExit()
{
CleanupMess();
if( g_Conf )
AppSaveSettings();
sMainFrame.RemoveEventHandler( &GetRecentIsoList() );
m_Resources = NULL;
return wxApp::OnExit();
}
// This method generates debug assertions if the MainFrame handle is NULL (typically
// indicating that PCSX2 is running in NoGUI mode, or that the main frame has been
// closed). In most cases you'll want to use HasMainFrame() to test for thread
@ -576,17 +605,22 @@ int Pcsx2App::OnExit()
// is a matter of programmer preference).
MainEmuFrame& Pcsx2App::GetMainFrame() const
{
pxAssume( ((uptr)GetTopWindow()) == ((uptr)m_MainFrame) );
pxAssume( m_MainFrame != NULL );
return *m_MainFrame;
MainEmuFrame* mainFrame = GetMainFramePtr();
pxAssume( mainFrame != NULL );
pxAssume( ((uptr)GetTopWindow()) == ((uptr)mainFrame) );
return *mainFrame;
}
GSFrame& Pcsx2App::GetGSFrame() const
GSFrame& Pcsx2App::GetGsFrame() const
{
pxAssume( m_gsFrame != NULL );
return *m_gsFrame;
GSFrame* gsFrame = (GSFrame*)wxWindow::FindWindowById( m_id_GsFrame );
pxAssume( gsFrame != NULL );
return *gsFrame;
}
void AppApplySettings( const AppConfig* oldconf )
{
AffinityAssert_AllowFromMain();
@ -626,12 +660,67 @@ void AppApplySettings( const AppConfig* oldconf )
}
}
int toSend = 0;
sApp.Source_SettingsApplied().Dispatch( toSend );
sApp.DispatchEvent( AppStatus_SettingsApplied );
suspend_core.Resume();
}
static wxFileConfig _dud_config;
class pxDudConfig : public wxConfigBase
{
protected:
wxString m_empty;
public:
virtual ~pxDudConfig() {}
virtual void SetPath(const wxString& ) {}
virtual const wxString& GetPath() const { return m_empty; }
virtual bool GetFirstGroup(wxString& , long& ) const { return false; }
virtual bool GetNextGroup (wxString& , long& ) const { return false; }
virtual bool GetFirstEntry(wxString& , long& ) const { return false; }
virtual bool GetNextEntry (wxString& , long& ) const { return false; }
virtual size_t GetNumberOfEntries(bool ) const { return 0; }
virtual size_t GetNumberOfGroups(bool ) const { return 0; }
virtual bool HasGroup(const wxString& ) const { return false; }
virtual bool HasEntry(const wxString& ) const { return false; }
virtual bool Flush(bool ) { return false; }
virtual bool RenameEntry(const wxString&, const wxString& ) { return false; }
virtual bool RenameGroup(const wxString&, const wxString& ) { return false; }
virtual bool DeleteEntry(const wxString&, bool bDeleteGroupIfEmpty = true) { return false; }
virtual bool DeleteGroup(const wxString& ) { return false; }
virtual bool DeleteAll() { return false; }
protected:
virtual bool DoReadString(const wxString& , wxString *) const { return false; }
virtual bool DoReadLong(const wxString& , long *) const { return false; }
virtual bool DoWriteString(const wxString& , const wxString& ) { return false; }
virtual bool DoWriteLong(const wxString& , long ) { return false; }
};
static pxDudConfig _dud_config;
// --------------------------------------------------------------------------------------
// AppIniSaver / AppIniLoader
// --------------------------------------------------------------------------------------
class AppIniSaver : public IniSaver
{
public:
AppIniSaver();
virtual ~AppIniSaver() throw() {}
};
class AppIniLoader : public IniLoader
{
public:
AppIniLoader();
virtual ~AppIniLoader() throw() {}
};
AppIniSaver::AppIniSaver()
: IniSaver( (GetAppConfig() != NULL) ? *GetAppConfig() : _dud_config )
@ -649,7 +738,7 @@ void AppLoadSettings()
AppIniLoader loader;
g_Conf->LoadSave( loader );
wxGetApp().Source_SettingsLoadSave().Dispatch( loader );
sApp.DispatchEvent( loader );
}
void AppSaveSettings()
@ -658,9 +747,13 @@ void AppSaveSettings()
AppIniSaver saver;
g_Conf->LoadSave( saver );
wxGetApp().Source_SettingsLoadSave().Dispatch( saver );
sApp.DispatchEvent( saver );
}
// Invokes the specified Pcsx2App method, or posts the method to the main thread if the calling
// thread is not Main. Action is blocking. For non-blocking method execution, use
// PostMethodToMainThread.
//
// This function works something like setjmp/longjmp, in that the return value indicates if the
// function actually executed the specified method or not.
//
@ -668,7 +761,7 @@ void AppSaveSettings()
// FALSE if the method was not posted to the main thread (meaning this IS the main thread!)
// TRUE if the method was posted.
//
bool Pcsx2App::SelfMethodInvoke( FnType_AppMethod method )
bool Pcsx2App::InvokeMethodOnMainThread( FnPtr_AppMethod method )
{
if( wxThread::IsMain() ) return false;
@ -680,7 +773,18 @@ bool Pcsx2App::SelfMethodInvoke( FnType_AppMethod method )
return true;
}
bool Pcsx2App::SelfMethodPost( FnType_AppMethod method )
// Invokes the specified Pcsx2App method, or posts the method to the main thread if the calling
// thread is not Main. Action is non-blocking. For blocking method execution, use
// InvokeMethodOnMainThread.
//
// This function works something like setjmp/longjmp, in that the return value indicates if the
// function actually executed the specified method or not.
//
// Returns:
// FALSE if the method was not posted to the main thread (meaning this IS the main thread!)
// TRUE if the method was posted.
//
bool Pcsx2App::PostMethodToMainThread( FnPtr_AppMethod method )
{
if( wxThread::IsMain() ) return false;
pxInvokeMethodEvent evt( method );
@ -688,20 +792,30 @@ bool Pcsx2App::SelfMethodPost( FnType_AppMethod method )
return true;
}
// Posts a method to the main thread; non-blocking. Post occurs even when called from the
// main thread.
void Pcsx2App::PostMethod( FnPtr_AppMethod method )
{
pxInvokeMethodEvent evt( method );
AddPendingEvent( evt );
}
void Pcsx2App::OpenGsPanel()
{
if( SelfMethodInvoke( &Pcsx2App::OpenGsPanel ) ) return;
if( InvokeMethodOnMainThread( &Pcsx2App::OpenGsPanel ) ) return;
if( m_gsFrame == NULL )
GSFrame* gsFrame = GetGsFramePtr();
if( gsFrame == NULL )
{
m_gsFrame = new GSFrame( m_MainFrame, L"PCSX2" );
m_gsFrame->SetFocus();
gsFrame = new GSFrame( GetMainFramePtr(), L"PCSX2" );
gsFrame->SetFocus();
m_id_GsFrame = gsFrame->GetId();
}
pxAssumeDev( !GetPluginManager().IsOpen( PluginId_GS ), "GS Plugin must be closed prior to opening a new Gs Panel!" );
m_gsFrame->Show();
pDsp = (uptr)m_gsFrame->GetViewport()->GetHandle();
gsFrame->Show();
pDsp = (uptr)gsFrame->GetViewport()->GetHandle();
// The "in the main window" quickie hack...
//pDsp = (uptr)m_MainFrame->m_background.GetHandle();
@ -709,11 +823,12 @@ void Pcsx2App::OpenGsPanel()
void Pcsx2App::CloseGsPanel()
{
if( SelfMethodInvoke( &Pcsx2App::CloseGsPanel ) ) return;
if( InvokeMethodOnMainThread( &Pcsx2App::CloseGsPanel ) ) return;
if( m_gsFrame != NULL && CloseViewportWithPlugins )
GSFrame* gsFrame = GetGsFramePtr();
if( (gsFrame != NULL) && CloseViewportWithPlugins )
{
if( GSPanel* woot = m_gsFrame->GetViewport() )
if( GSPanel* woot = gsFrame->GetViewport() )
woot->Destroy();
}
}
@ -721,22 +836,22 @@ void Pcsx2App::CloseGsPanel()
void Pcsx2App::OnGsFrameClosed()
{
CoreThread.Suspend();
m_gsFrame = NULL;
m_id_GsFrame = wxID_ANY;
}
void Pcsx2App::OnProgramLogClosed()
{
if( m_ProgramLogBox == NULL ) return;
if( m_id_ProgramLogBox == wxID_ANY ) return;
m_id_ProgramLogBox = wxID_ANY;
DisableWindowLogging();
m_ProgramLogBox = NULL;
}
void Pcsx2App::OnMainFrameClosed()
{
// Nothing threaded depends on the mainframe (yet) -- it all passes through the main wxApp
// message handler. But that might change in the future.
if( m_MainFrame == NULL ) return;
m_MainFrame = NULL;
//if( m_id_MainFrame == wxID_ANY ) return;
m_id_MainFrame = wxID_ANY;
}
// --------------------------------------------------------------------------------------
@ -904,3 +1019,39 @@ SysCoreAllocations& GetSysCoreAlloc()
{
return *wxGetApp().m_CoreAllocs;
}
// --------------------------------------------------------------------------------------
// pxStuckThreadEvent Implementation
// --------------------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS( pxStuckThreadEvent, BaseMessageBoxEvent )
pxStuckThreadEvent::pxStuckThreadEvent()
: BaseMessageBoxEvent()
, m_Thread( *(PersistentThread*)NULL )
{
}
pxStuckThreadEvent::pxStuckThreadEvent( MsgboxEventResult& instdata, PersistentThread& thr )
: BaseMessageBoxEvent( instdata, wxEmptyString )
, m_Thread( thr )
{
}
pxStuckThreadEvent::pxStuckThreadEvent( PersistentThread& thr )
: BaseMessageBoxEvent()
, m_Thread( thr )
{
}
pxStuckThreadEvent::pxStuckThreadEvent( const pxStuckThreadEvent& src )
: BaseMessageBoxEvent( src )
, m_Thread( src.m_Thread )
{
}
int pxStuckThreadEvent::_DoDialog() const
{
return 1;
//return Dialogs::StuckThreadDialog( m_Thread ).ShowModal();
}

View File

@ -124,7 +124,8 @@ class BaseApplicableConfigPanel : public wxPanelWithHelpers
protected:
int m_OwnerPage;
wxBookCtrlBase* m_OwnerBook;
EventListenerBinding<int> m_Listener_SettingsApplied;
EventListenerHelper_AppStatus<BaseApplicableConfigPanel> m_AppStatusHelper;
public:
virtual ~BaseApplicableConfigPanel() throw();
@ -148,13 +149,16 @@ public:
// If no exceptions are thrown, then the operation is assumed a success. :)
virtual void Apply()=0;
// This method is bound to the ApplySettings event from the PCSX2 app manager.
// Mandatory override: As a rule for proper interface design, all deriving classes need
// to implement this function. There's no implementation of an options/settings panel
// that does not heed the changes of application status/settings changes. ;)
//
// Note: This method *will* be called automatically after a successful Apply, but will not
// be called after a failed Apply (canceled due to error).
virtual void OnSettingsChanged()=0;
virtual void AppStatusEvent_OnSettingsApplied()=0;
protected:
static void __evt_fastcall OnSettingsApplied( void* obj, int& evt );
virtual void AppStatusEvent_OnSettingsLoadSave( const AppSettingsEventInfo& ) {}
virtual void AppStatusEvent_OnExit() {}
void Init();
};

View File

@ -695,14 +695,19 @@ void ConsoleLogFrame::DoFlushQueue()
ConsoleLogFrame* Pcsx2App::GetProgramLog()
{
return m_ProgramLogBox;
return (ConsoleLogFrame*) wxWindow::FindWindowById( m_id_ProgramLogBox );
}
const ConsoleLogFrame* Pcsx2App::GetProgramLog() const
{
return (const ConsoleLogFrame*) wxWindow::FindWindowById( m_id_ProgramLogBox );
}
void Pcsx2App::ProgramLog_PostEvent( wxEvent& evt )
{
// New console log object model makes this check obsolete:
//if( m_ProgramLogBox == NULL ) return;
m_ProgramLogBox->GetEventHandler()->AddPendingEvent( evt );
GetProgramLog()->GetEventHandler()->AddPendingEvent( evt );
}
// --------------------------------------------------------------------------------------
@ -828,17 +833,20 @@ static const IConsoleWriter ConsoleWriter_WindowAndFile =
void Pcsx2App::EnableAllLogging() const
{
const bool logBoxOpen = (GetProgramLog() != NULL);
if( emuLog )
Console_SetActiveHandler( (m_ProgramLogBox!=NULL) ? (IConsoleWriter&)ConsoleWriter_WindowAndFile : (IConsoleWriter&)ConsoleWriter_File );
Console_SetActiveHandler( logBoxOpen ? (IConsoleWriter&)ConsoleWriter_WindowAndFile : (IConsoleWriter&)ConsoleWriter_File );
else
Console_SetActiveHandler( (m_ProgramLogBox!=NULL) ? (IConsoleWriter&)ConsoleWriter_Window : (IConsoleWriter&)ConsoleWriter_Stdout );
Console_SetActiveHandler( logBoxOpen ? (IConsoleWriter&)ConsoleWriter_Window : (IConsoleWriter&)ConsoleWriter_Stdout );
}
// Used to disable the emuLog disk logger, typically used when disabling or re-initializing the
// emuLog file handle. Call SetConsoleLogging to re-enable the disk logger when finished.
void Pcsx2App::DisableDiskLogging() const
{
Console_SetActiveHandler( (m_ProgramLogBox!=NULL) ? (IConsoleWriter&)ConsoleWriter_Window : (IConsoleWriter&)ConsoleWriter_Stdout );
const bool logBoxOpen = (GetProgramLog() != NULL);
Console_SetActiveHandler( logBoxOpen ? (IConsoleWriter&)ConsoleWriter_Window : (IConsoleWriter&)ConsoleWriter_Stdout );
// Semi-hack: It's possible, however very unlikely, that a secondary thread could attempt
// to write to the logfile just before we disable logging, and would thus have a pending write

View File

@ -89,12 +89,9 @@ public:
// --------------------------------------------------------------------------------------
// pxLogTextCtrl
// --------------------------------------------------------------------------------------
class pxLogTextCtrl : public wxTextCtrl
class pxLogTextCtrl : public wxTextCtrl, public IEventListener_CoreThread, public IEventListener_Plugins
{
protected:
CmdEvt_ListenerBinding m_Listener_CoreThreadStatus;
EventListenerBinding<PluginEventType> m_Listener_CorePluginStatus;
#ifdef __WXMSW__
int m_win32_LinesPerPage;
int m_win32_LinesPerScroll;
@ -116,9 +113,9 @@ protected:
virtual void OnThumbTrack(wxScrollWinEvent& event);
virtual void OnThumbRelease(wxScrollWinEvent& event);
virtual void OnResize( wxSizeEvent& evt );
static void __evt_fastcall OnCoreThreadStatusChanged( void* obj, wxCommandEvent& evt );
static void __evt_fastcall OnCorePluginStatusChanged( void* obj, PluginEventType& evt );
void DispatchEvent( const CoreThreadStatus& status );
void DispatchEvent( const PluginEventType& evt );
};
// --------------------------------------------------------------------------------------

View File

@ -67,6 +67,7 @@ protected:
virtual void OnDoubleClicked( wxCommandEvent& evt );
};
namespace Dialogs
{
class AboutBoxDialog: public wxDialogWithHelpers
@ -114,8 +115,41 @@ namespace Dialogs
public:
AssertionDialog( const wxString& text, const wxString& stacktrace );
virtual ~AssertionDialog() throw() {}
};
// There are two types of stuck threads:
// * Threads stuck on any action that is not a cancellation.
// * Threads stuck trying to cancel.
//
// The former means we can provide a "cancel" action for the user, which would itself
// open a new dialog in the latter category. The latter means that there's really nothing
// we can do, since pthreads API provides no good way for killing threads. The only
// valid options for the user in that case is to either wait (boring!) or kill the
// process (awesome!).
enum StuckThreadActionType
{
// Allows the user to attempt a cancellation of a stuck thread. This should only be
// used on threads which are not already stuck during a cancellation action (ie, suspension
// or other job requests). Also, if the running thread is known to not have any
// cancellation points then this shouldn't be used either.
StacT_TryCancel,
// Allows the user to kill the entire process for a stuck thread. Use this for any
// thread which has failed to cancel in a reasonable timeframe, or for any stuck action
// if the thread is known to have no cancellation points.
StacT_KillProcess,
};
class StuckThreadDialog : public wxDialogWithHelpers,
public EventListener_Thread
{
public:
StuckThreadDialog( wxWindow* parent, StuckThreadActionType action, Threading::PersistentThread& stuck_thread );
virtual ~StuckThreadDialog() throw() {}
protected:
void OnThreadCleanup();
};
}

View File

@ -0,0 +1,52 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "App.h"
#include "Dialogs/ModalPopups.h"
using namespace pxSizerFlags;
using namespace Threading;
Dialogs::StuckThreadDialog::StuckThreadDialog( wxWindow* parent, StuckThreadActionType action, PersistentThread& stuck_thread )
: wxDialogWithHelpers( parent, _("PCSX2 Thread is not responding"), wxVERTICAL )
{
//m_idealWidth = 720;
stuck_thread.AddListener( this );
*this += Heading( wxsFormat(
pxE( ".Dialog:StuckThread:Heading",
L"The thread '%s' is not responding. It could be deadlocked, or it might "
L"just be running *really* slowly."
),
stuck_thread.GetName()
) );
*this += Heading(
L"\nDo you want to stop the program [Yes/No]?"
L"\nOr press [Ignore] to suppress further assertions."
);
*this += new ModalButtonPanel( this, MsgButtons().Cancel().Custom(L"Wait") ) | StdCenter();
if( wxWindow* idyes = FindWindowById( wxID_YES ) )
idyes->SetFocus();
}
void Dialogs::StuckThreadDialog::OnThreadCleanup()
{
}

View File

@ -22,6 +22,9 @@
void GSPanel::InitDefaultAccelerators()
{
// Note! These don't really work yet due to some hacks to get things working for
// old legacy PAD plugins. (the global accelerator tables are used instead) --air
typedef KeyAcceleratorCode AAC;
m_Accels.Map( AAC( WXK_F1 ), "States_FreezeCurrentSlot" );
@ -46,7 +49,6 @@ void GSPanel::InitDefaultAccelerators()
GSPanel::GSPanel( wxWindow* parent )
: wxWindow()
, m_Listener_SettingsApplied ( wxGetApp().Source_SettingsApplied(), EventListener<int> ( this, OnSettingsApplied ) )
, m_HideMouseTimer( this )
{
m_CursorShown = true;
@ -211,15 +213,7 @@ void GSPanel::OnFocusLost( wxFocusEvent& evt )
DoShowMouse();
}
void __evt_fastcall GSPanel::OnSettingsApplied( void* obj, int& evt )
{
if( obj == NULL ) return;
GSPanel* panel = (GSPanel*)obj;
panel->DoSettingsApplied();
}
void GSPanel::DoSettingsApplied()
void GSPanel::AppStatusEvent_OnSettingsApplied()
{
if( IsBeingDeleted() ) return;
DoResize();
@ -237,7 +231,6 @@ GSFrame::GSFrame(wxWindow* parent, const wxString& title)
(g_Conf->GSWindow.DisableResizeBorders ? 0 : wxRESIZE_BORDER) | wxCAPTION | wxCLIP_CHILDREN |
wxSYSTEM_MENU | wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxCLOSE_BOX
)
, m_Listener_SettingsApplied( wxGetApp().Source_SettingsApplied(), EventListener<int> ( this, OnSettingsApplied ) )
, m_timer_UpdateTitle( this )
{
SetIcons( wxGetApp().GetIconBundle() );
@ -246,14 +239,18 @@ GSFrame::GSFrame(wxWindow* parent, const wxString& title)
SetBackgroundColour( *wxBLACK );
wxStaticText* label = new wxStaticText( this, wxID_ANY, _("GS Output is Disabled!") );
label->SetName(L"OutputDisabledLabel");
m_id_OutputDisabled = label->GetId();
label->SetFont( *new wxFont( 20, wxDEFAULT, wxNORMAL, wxBOLD ) );
label->SetForegroundColour( *wxWHITE );
label->Show( EmuConfig.GS.DisableOutput );
GSPanel* gsPanel = new GSPanel( this );
gsPanel->Show( !EmuConfig.GS.DisableOutput );
m_gspanel_id = gsPanel->GetId();
m_id_gspanel = gsPanel->GetId();
// TODO -- Implement this GS window status window! Whee.
// (main concern is retaining proper client window sizes when closing/re-opening the window).
//m_statusbar = CreateStatusBar( 2 );
//Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler (GSFrame::OnCloseWindow) );
Connect( wxEVT_MOVE, wxMoveEventHandler (GSFrame::OnMove) );
@ -267,6 +264,11 @@ GSFrame::~GSFrame() throw()
{
}
wxStaticText* GSFrame::GetLabel_OutputDisabled() const
{
return (wxStaticText*)FindWindowById( m_id_OutputDisabled );
}
// overrides base Show behavior.
bool GSFrame::Show( bool shown )
{
@ -277,12 +279,15 @@ bool GSFrame::Show( bool shown )
if( gsPanel == NULL || gsPanel->IsBeingDeleted() )
{
gsPanel = new GSPanel( this );
m_gspanel_id = gsPanel->GetId();
m_id_gspanel = gsPanel->GetId();
}
gsPanel->Show( !EmuConfig.GS.DisableOutput );
gsPanel->DoResize();
gsPanel->SetFocus();
if( wxStaticText* label = GetLabel_OutputDisabled() )
label->Show( !EmuConfig.GS.DisableOutput );
m_timer_UpdateTitle.Start( 333 );
}
@ -294,27 +299,19 @@ bool GSFrame::Show( bool shown )
return _parent::Show( shown );
}
void __evt_fastcall GSFrame::OnSettingsApplied( void* obj, int& evt )
{
if( obj == NULL ) return;
GSFrame* frame = (GSFrame*)obj;
frame->DoSettingsApplied();
}
void GSFrame::DoSettingsApplied()
void GSFrame::AppStatusEvent_OnSettingsApplied()
{
if( IsBeingDeleted() ) return;
ShowFullScreen( g_Conf->GSWindow.DefaultToFullscreen );
Show( !g_Conf->GSWindow.CloseOnEsc || ((g_plugins==NULL) || !SysHasValidState()) );
if( wxStaticText* label = (wxStaticText*)FindWindowByName(L"OutputDisabledLabel") )
if( wxStaticText* label = GetLabel_OutputDisabled() )
label->Show( !EmuConfig.GS.DisableOutput );
}
GSPanel* GSFrame::GetViewport()
{
return (GSPanel*)FindWindowById( m_gspanel_id );
return (GSPanel*)FindWindowById( m_id_gspanel );
}
void GSFrame::OnUpdateTitle( wxTimerEvent& evt )

View File

@ -111,7 +111,8 @@ void MainEmuFrame::OnMoveAround( wxMoveEvent& evt )
if( g_Conf->ProgLogBox.AutoDock )
{
g_Conf->ProgLogBox.DisplayPosition = GetRect().GetTopRight();
wxGetApp().GetProgramLog()->SetPosition( g_Conf->ProgLogBox.DisplayPosition );
if( ConsoleLogFrame* proglog = wxGetApp().GetProgramLog() )
proglog->SetPosition( g_Conf->ProgLogBox.DisplayPosition );
}
//evt.Skip();
@ -135,6 +136,7 @@ void MainEmuFrame::ConnectMenus()
ConnectMenu( MenuId_Config_SysSettings, Menu_ConfigSettings_Click );
ConnectMenu( MenuId_Config_AppSettings, Menu_AppSettings_Click );
ConnectMenu( MenuId_Config_BIOS, Menu_SelectBios_Click );
ConnectMenu( MenuId_Config_ResetAll, Menu_ResetAllSettings_Click );
ConnectMenu( MenuId_Config_Multitap0Toggle, Menu_MultitapToggle_Click );
ConnectMenu( MenuId_Config_Multitap1Toggle, Menu_MultitapToggle_Click );
@ -193,44 +195,30 @@ void MainEmuFrame::InitLogBoxPosition( AppConfig::ConsoleLogOptions& conf )
}
}
void __evt_fastcall MainEmuFrame::OnCoreThreadStatusChanged( void* obj, wxCommandEvent& evt )
void MainEmuFrame::DispatchEvent( const PluginEventType& plugin_evt )
{
if( obj == NULL ) return;
MainEmuFrame* mframe = (MainEmuFrame*)obj;
if( !pxAssertMsg( mframe->GetMenuBar()!=NULL, "Mainframe menu bar is NULL!" ) ) return;
if( !pxAssertMsg( GetMenuBar()!=NULL, "Mainframe menu bar is NULL!" ) ) return;
mframe->ApplyCoreStatus();
//ApplyCoreStatus();
ApplyPluginStatus();
}
void __evt_fastcall MainEmuFrame::OnCorePluginStatusChanged( void* obj, PluginEventType& evt )
void MainEmuFrame::DispatchEvent( const CoreThreadStatus& status )
{
if( obj == NULL ) return;
if( (evt != PluginsEvt_Loaded) && (evt != PluginsEvt_Unloaded) ) return; // everything else we don't care about
MainEmuFrame& mframe = *(MainEmuFrame*)obj;
if( !pxAssertMsg( mframe.GetMenuBar()!=NULL, "Mainframe menu bar is NULL!" ) ) return;
//mframe.ApplyCoreStatus();
mframe.ApplyPluginStatus();
if( !pxAssertMsg( GetMenuBar()!=NULL, "Mainframe menu bar is NULL!" ) ) return;
ApplyCoreStatus();
}
void __evt_fastcall MainEmuFrame::OnSettingsApplied( void* obj, int& evt )
void MainEmuFrame::AppStatusEvent_OnSettingsLoadSave()
{
if( obj == NULL ) return;
MainEmuFrame* mframe = (MainEmuFrame*)obj;
if( !pxAssertMsg( mframe->GetMenuBar()!=NULL, "Mainframe menu bar is NULL!" ) ) return;
mframe->ApplySettings();
}
void __evt_fastcall MainEmuFrame::OnSettingsLoadSave( void* obj, IniInterface& evt )
{
if( obj == NULL ) return;
//MainEmuFrame* mframe = (MainEmuFrame*)obj;
// nothing to do here right now.
}
void MainEmuFrame::AppStatusEvent_OnSettingsApplied()
{
ApplySettings();
}
static int GetPluginMenuId_Settings( PluginsEnum_t pid )
{
return MenuId_PluginBase_Settings + ((int)pid * PluginMenuId_Interval);
@ -265,11 +253,9 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title)
, m_MenuItem_Console( *new wxMenuItem( &m_menuMisc, MenuId_Console, L"Show Console", wxEmptyString, wxITEM_CHECK ) )
, m_MenuItem_Console_Stdio( *new wxMenuItem( &m_menuMisc, MenuId_Console_Stdio, L"Console to Stdio", wxEmptyString, wxITEM_CHECK ) )
, m_Listener_CoreThreadStatus ( wxGetApp().Source_CoreThreadStatus(), CmdEvt_Listener ( this, OnCoreThreadStatusChanged ) )
, m_Listener_CorePluginStatus ( wxGetApp().Source_CorePluginStatus(), EventListener<PluginEventType> ( this, OnCorePluginStatusChanged ) )
, m_Listener_SettingsApplied ( wxGetApp().Source_SettingsApplied(), EventListener<int> ( this, OnSettingsApplied ) )
, m_Listener_SettingsLoadSave ( wxGetApp().Source_SettingsLoadSave(), EventListener<IniInterface> ( this, OnSettingsLoadSave ) )
{
m_RestartEmuOnDelete = false;
for( int i=0; i<PluginId_Count; ++i )
m_PluginMenuPacks[i].Populate( (PluginsEnum_t)i );
@ -410,7 +396,7 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title)
m_menuConfig.Append(MenuId_Config_Multitap1Toggle, _("Multitap 2"), wxEmptyString, wxITEM_CHECK );
m_menuConfig.AppendSeparator();
m_menuConfig.Append(MenuId_Config_ResetAll, _("Reset all..."),
m_menuConfig.Append(MenuId_Config_ResetAll, _("Clear all settings..."),
_("Clears all PCSX2 settings and re-runs the startup wizard."));
// ------------------------------------------------------------------------
@ -456,6 +442,13 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title)
MainEmuFrame::~MainEmuFrame() throw()
{
m_menuCDVD.Remove( MenuId_IsoSelector );
if( m_RestartEmuOnDelete )
{
sApp.SetExitOnFrameDelete( false );
sApp.PostMethod( &Pcsx2App::DetectCpuAndUserMode );
sApp.WipeUserModeSettings();
}
}
// ----------------------------------------------------------------------------

View File

@ -33,13 +33,12 @@ extern LimiterModeType g_LimiterMode;
// --------------------------------------------------------------------------------------
// GSPanel
// --------------------------------------------------------------------------------------
class GSPanel : public wxWindow
class GSPanel : public wxWindow, public IEventListener_AppStatus
{
typedef wxWindow _parent;
protected:
AcceleratorDictionary m_Accels;
EventListenerBinding<int> m_Listener_SettingsApplied;
wxTimer m_HideMouseTimer;
bool m_CursorShown;
bool m_HasFocus;
@ -52,7 +51,7 @@ public:
void DoShowMouse();
protected:
static void __evt_fastcall OnSettingsApplied( void* obj, int& evt );
void AppStatusEvent_OnSettingsApplied();
#ifdef __WXMSW__
virtual WXLRESULT MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam);
@ -60,8 +59,6 @@ protected:
void InitDefaultAccelerators();
void DoSettingsApplied();
void OnCloseWindow( wxCloseEvent& evt );
void OnResize(wxSizeEvent& event);
void OnShowMouse( wxMouseEvent& evt );
@ -74,15 +71,16 @@ protected:
// --------------------------------------------------------------------------------------
// GSFrame
// --------------------------------------------------------------------------------------
class GSFrame : public wxFrame
class GSFrame : public wxFrame, public IEventListener_AppStatus
{
typedef wxFrame _parent;
protected:
EventListenerBinding<int> m_Listener_SettingsApplied;
wxTimer m_timer_UpdateTitle;
wxWindowID m_gspanel_id;
wxWindowID m_id_gspanel;
wxWindowID m_id_OutputDisabled;
wxStaticText* m_label_Disabled;
wxStatusBar* m_statusbar;
public:
GSFrame(wxWindow* parent, const wxString& title);
@ -91,6 +89,7 @@ public:
GSPanel* GetViewport();
bool Show( bool shown=true );
wxStaticText* GetLabel_OutputDisabled() const;
protected:
void OnMove( wxMoveEvent& evt );
@ -98,9 +97,7 @@ protected:
void OnActivate( wxActivateEvent& evt );
void OnUpdateTitle( wxTimerEvent& evt );
void DoSettingsApplied();
static void __evt_fastcall OnSettingsApplied( void* obj, int& evt );
void AppStatusEvent_OnSettingsApplied();
};
struct PluginMenuAddition
@ -159,9 +156,14 @@ public:
// --------------------------------------------------------------------------------------
// MainEmuFrame
// --------------------------------------------------------------------------------------
class MainEmuFrame : public wxFrame
class MainEmuFrame : public wxFrame,
public virtual IEventListener_Plugins,
public virtual IEventListener_CoreThread,
public virtual IEventListener_AppStatus
{
protected:
bool m_RestartEmuOnDelete;
wxStatusBar& m_statusbar;
wxStaticBitmap m_background;
@ -182,10 +184,10 @@ protected:
PerPluginMenuInfo m_PluginMenuPacks[PluginId_Count];
CmdEvt_ListenerBinding m_Listener_CoreThreadStatus;
EventListenerBinding<PluginEventType> m_Listener_CorePluginStatus;
EventListenerBinding<int> m_Listener_SettingsApplied;
EventListenerBinding<IniInterface> m_Listener_SettingsLoadSave;
virtual void DispatchEvent( const PluginEventType& plugin_evt );
virtual void DispatchEvent( const CoreThreadStatus& status );
virtual void AppStatusEvent_OnSettingsLoadSave();
virtual void AppStatusEvent_OnSettingsApplied();
public:
MainEmuFrame(wxWindow* parent, const wxString& title);
@ -197,11 +199,6 @@ public:
void UpdateIsoSrcSelection();
protected:
static void __evt_fastcall OnCoreThreadStatusChanged( void* obj, wxCommandEvent& evt );
static void __evt_fastcall OnCorePluginStatusChanged( void* obj, PluginEventType& evt );
static void __evt_fastcall OnSettingsApplied( void* obj, int& evt );
static void __evt_fastcall OnSettingsLoadSave( void* obj, IniInterface& evt );
void ApplySettings();
void ApplyCoreStatus();
void ApplyPluginStatus();
@ -218,6 +215,7 @@ protected:
void Menu_ConfigSettings_Click(wxCommandEvent &event);
void Menu_AppSettings_Click(wxCommandEvent &event);
void Menu_SelectBios_Click(wxCommandEvent &event);
void Menu_ResetAllSettings_Click(wxCommandEvent &event);
void Menu_IsoBrowse_Click(wxCommandEvent &event);
void Menu_SkipBiosToggle_Click(wxCommandEvent &event);

View File

@ -54,6 +54,87 @@ void MainEmuFrame::Menu_SelectBios_Click(wxCommandEvent &event)
AppOpenDialog<BiosSelectorDialog>( this );
}
static void WipeSettings()
{
UnloadPlugins();
wxGetApp().CleanupRestartable();
wxGetApp().CleanupResources();
wxRemoveFile( GetSettingsFilename() );
// FIXME: wxRmdir doesn't seem to work here for some reason (possible file sharing issue
// with a plugin that leaves a file handle dangling maybe?). But deleting the inis folder
// manually from explorer does work. Can't think of a good work-around at the moment. --air
//wxRmdir( GetSettingsFolder().ToString() );
}
class RestartEverything_WhenCoreThreadStops : public IEventListener_CoreThread,
public virtual IDeletableObject
{
public:
RestartEverything_WhenCoreThreadStops() {}
virtual ~RestartEverything_WhenCoreThreadStops() throw() {}
protected:
virtual void OnCoreStatus_Stopped()
{
wxGetApp().DeleteObject( this );
WipeSettings();
}
};
class CancelCoreThread_WhenSaveStateDone : public IEventListener_CoreThread,
public IDeletableObject
{
public:
virtual ~CancelCoreThread_WhenSaveStateDone() throw() {}
void OnCoreStatus_Resumed()
{
wxGetApp().DeleteObject( this );
CoreThread.Cancel();
}
};
void MainEmuFrame::Menu_ResetAllSettings_Click(wxCommandEvent &event)
{
if( IsBeingDeleted() || m_RestartEmuOnDelete ) return;
ScopedCoreThreadSuspend suspender;
if( !Msgbox::OkCancel(
pxE( ".Popup Warning:DeleteSettings",
L"WARNING!! This option will delete *ALL* settings for PCSX2 and force PCSX2 to restart, losing any current emulation progress. Are you absolutely sure?"
L"\n\n(note: settings for plugins are unaffected)"
),
_("Reset all settings?") ) )
{
suspender.Resume();
return;
}
m_RestartEmuOnDelete = true;
Destroy();
if( CoreThread.IsRunning() )
{
new RestartEverything_WhenCoreThreadStops();
if( StateCopy_IsBusy() )
{
new CancelCoreThread_WhenSaveStateDone();
throw Exception::CancelEvent( "Savestate in progress, app restart event delayed until action is complete." );
}
CoreThread.Cancel();
}
else
{
WipeSettings();
}
}
void MainEmuFrame::Menu_CdvdSource_Click( wxCommandEvent &event )
{
CDVD_SourceType newSource = CDVDsrc_NoDisc;

View File

@ -147,18 +147,18 @@ int pxMessageBoxEvent::_DoDialog() const
IMPLEMENT_DYNAMIC_CLASS( pxAssertionEvent, BaseMessageBoxEvent )
pxAssertionEvent::pxAssertionEvent()
: BaseMessageBoxEvent( pxEvt_Assertion )
: BaseMessageBoxEvent( )
{
}
pxAssertionEvent::pxAssertionEvent( MsgboxEventResult& instdata, const wxString& content, const wxString& trace )
: BaseMessageBoxEvent( pxEvt_Assertion )
: BaseMessageBoxEvent( instdata, content )
, m_Stacktrace( trace )
{
}
pxAssertionEvent::pxAssertionEvent( const wxString& content, const wxString& trace )
: BaseMessageBoxEvent( pxEvt_Assertion, content )
: BaseMessageBoxEvent( content )
, m_Stacktrace( trace )
{
}
@ -188,7 +188,7 @@ int pxAssertionEvent::_DoDialog() const
namespace Msgbox
{
static int ThreadedMessageBox( BaseMessageBoxEvent& evt )
int ShowModal( BaseMessageBoxEvent& evt )
{
MsgboxEventResult instdat;
evt.SetInstData( instdat );
@ -207,7 +207,7 @@ namespace Msgbox
return instdat.result;
}
static int ThreadedMessageBox( const wxString& title, const wxString& content, const MsgButtons& buttons )
static int ShowModal( const wxString& title, const wxString& content, const MsgButtons& buttons )
{
// must pass the message to the main gui thread, and then stall this thread, to avoid
// threaded chaos where our thread keeps running while the popup is awaiting input.
@ -228,7 +228,7 @@ namespace Msgbox
if( wxThread::IsMain() )
pxMessageDialog( caption, text, buttons );
else
ThreadedMessageBox( caption, text, buttons );
ShowModal( caption, text, buttons );
return false;
}
@ -244,7 +244,7 @@ namespace Msgbox
}
else
{
return wxID_OK == ThreadedMessageBox( caption, text, buttons );
return wxID_OK == ShowModal( caption, text, buttons );
}
}
@ -258,13 +258,13 @@ namespace Msgbox
}
else
{
return wxID_YES == ThreadedMessageBox( caption, text, buttons );
return wxID_YES == ShowModal( caption, text, buttons );
}
}
int Assertion( const wxString& text, const wxString& stacktrace )
{
pxAssertionEvent tevt( text, stacktrace );
return ThreadedMessageBox( tevt );
return ShowModal( tevt );
}
}

View File

@ -24,7 +24,6 @@
#include <wx/filepicker.h>
#include <wx/listbox.h>
// ------------------------------------------------------------------------
Panels::BaseSelectorPanel::BaseSelectorPanel( wxWindow* parent )
: BaseApplicableConfigPanel( parent, wxVERTICAL )
@ -127,7 +126,7 @@ void Panels::BiosSelectorPanel::Apply()
g_Conf->BaseFilenames.Bios = (*m_BiosList)[(int)m_ComboBox.GetClientData(sel)];
}
void Panels::BiosSelectorPanel::OnSettingsChanged()
void Panels::BiosSelectorPanel::AppStatusEvent_OnSettingsApplied()
{
}

View File

@ -74,7 +74,7 @@ namespace Panels
UsermodeSelectionPanel( wxWindow* parent, bool isFirstTime = true );
void Apply();
void OnSettingsChanged();
void AppStatusEvent_OnSettingsApplied();
};
//////////////////////////////////////////////////////////////////////////////////////////
@ -90,7 +90,7 @@ namespace Panels
LanguageSelectionPanel( wxWindow* parent );
void Apply();
void OnSettingsChanged();
void AppStatusEvent_OnSettingsApplied();
};
//////////////////////////////////////////////////////////////////////////////////////////
@ -104,7 +104,7 @@ namespace Panels
public:
CpuPanelEE( wxWindow* parent );
void Apply();
void OnSettingsChanged();
void AppStatusEvent_OnSettingsApplied();
};
class CpuPanelVU : public BaseApplicableConfigPanel
@ -116,7 +116,7 @@ namespace Panels
public:
CpuPanelVU( wxWindow* parent );
void Apply();
void OnSettingsChanged();
void AppStatusEvent_OnSettingsApplied();
};
class BaseAdvancedCpuOptions : public BaseApplicableConfigPanel
@ -146,7 +146,7 @@ namespace Panels
AdvancedOptionsFPU( wxWindow* parent );
virtual ~AdvancedOptionsFPU() throw() { }
void Apply();
void OnSettingsChanged();
void AppStatusEvent_OnSettingsApplied();
};
class AdvancedOptionsVU : public BaseAdvancedCpuOptions
@ -155,7 +155,7 @@ namespace Panels
AdvancedOptionsVU( wxWindow* parent );
virtual ~AdvancedOptionsVU() throw() { }
void Apply();
void OnSettingsChanged();
void AppStatusEvent_OnSettingsApplied();
};
// --------------------------------------------------------------------------------------
@ -176,7 +176,7 @@ namespace Panels
virtual ~FrameSkipPanel() throw() {}
void Apply();
void OnSettingsChanged();
void AppStatusEvent_OnSettingsApplied();
};
// --------------------------------------------------------------------------------------
@ -202,7 +202,7 @@ namespace Panels
virtual ~FramelimiterPanel() throw() {}
void Apply();
void OnSettingsChanged();
void AppStatusEvent_OnSettingsApplied();
};
// --------------------------------------------------------------------------------------
@ -227,7 +227,7 @@ namespace Panels
GSWindowSettingsPanel( wxWindow* parent );
virtual ~GSWindowSettingsPanel() throw() {}
void Apply();
void OnSettingsChanged();
void AppStatusEvent_OnSettingsApplied();
};
class VideoPanel : public BaseApplicableConfigPanel
@ -242,7 +242,7 @@ namespace Panels
VideoPanel( wxWindow* parent );
virtual ~VideoPanel() throw() {}
void Apply();
void OnSettingsChanged();
void AppStatusEvent_OnSettingsApplied();
protected:
void OnOpenWindowSettings( wxCommandEvent& evt );
@ -273,8 +273,8 @@ namespace Panels
SpeedHacksPanel( wxWindow* parent );
void Apply();
void EnableStuff();
void OnSettingsChanged();
void OnSettingsChanged( const Pcsx2Config::SpeedhackOptions& opt );
void AppStatusEvent_OnSettingsApplied();
void AppStatusEvent_OnSettingsApplied( const Pcsx2Config::SpeedhackOptions& opt );
protected:
const wxChar* GetEEcycleSliderMsg( int val );
@ -299,7 +299,7 @@ namespace Panels
public:
GameFixesPanel( wxWindow* parent );
void Apply();
void OnSettingsChanged();
void AppStatusEvent_OnSettingsApplied();
};
//////////////////////////////////////////////////////////////////////////////////////////
@ -319,7 +319,7 @@ namespace Panels
virtual ~DirPickerPanel() throw() { }
void Apply();
void OnSettingsChanged();
void AppStatusEvent_OnSettingsApplied();
void Reset();
wxDirName GetPath() const;
@ -394,7 +394,7 @@ namespace Panels
protected:
virtual void Apply();
virtual void OnSettingsChanged();
virtual void AppStatusEvent_OnSettingsApplied();
virtual void DoRefresh();
virtual bool ValidateEnumerationStatus();
};
@ -402,7 +402,8 @@ namespace Panels
// --------------------------------------------------------------------------------------
// PluginSelectorPanel
// --------------------------------------------------------------------------------------
class PluginSelectorPanel: public BaseSelectorPanel
class PluginSelectorPanel: public BaseSelectorPanel,
public IEventListener_Plugins
{
protected:
// ----------------------------------------------------------------------------
@ -490,8 +491,6 @@ namespace Panels
ScopedPtr<wxArrayString> m_FileList; // list of potential plugin files
ScopedPtr<EnumThread> m_EnumeratorThread;
EventListenerBinding<PluginEventType> m_Listener_CorePluginStatus;
public:
virtual ~PluginSelectorPanel() throw();
PluginSelectorPanel( wxWindow* parent, int idealWidth=wxDefaultCoord );
@ -500,7 +499,7 @@ namespace Panels
void Apply();
protected:
static void __evt_fastcall OnCorePluginStatusChanged( void* obj, PluginEventType& evt );
void DispatchEvent( const PluginEventType& evt );
void OnConfigure_Clicked( wxCommandEvent& evt );
void OnShowStatusBar( wxCommandEvent& evt );
@ -509,7 +508,7 @@ namespace Panels
virtual void OnProgress( wxCommandEvent& evt );
virtual void OnEnumComplete( wxCommandEvent& evt );
virtual void OnSettingsChanged();
virtual void AppStatusEvent_OnSettingsApplied();
virtual void DoRefresh();
virtual bool ValidateEnumerationStatus();

View File

@ -106,7 +106,7 @@ Panels::AdvancedOptionsFPU::AdvancedOptionsFPU( wxWindow* parent )
m_RoundModePanel->Realize();
m_ClampModePanel->Realize();
OnSettingsChanged();
AppStatusEvent_OnSettingsApplied();
}
@ -121,7 +121,7 @@ Panels::AdvancedOptionsVU::AdvancedOptionsVU( wxWindow* parent )
m_RoundModePanel->Realize();
m_ClampModePanel->Realize();
OnSettingsChanged();
AppStatusEvent_OnSettingsApplied();
}
Panels::CpuPanelEE::CpuPanelEE( wxWindow* parent )
@ -175,7 +175,7 @@ Panels::CpuPanelEE::CpuPanelEE( wxWindow* parent )
*this += new wxStaticLine( this ) | wxSF.Border(wxALL, 18).Expand();
*this += new AdvancedOptionsFPU( this ) | StdExpand();
OnSettingsChanged();
AppStatusEvent_OnSettingsApplied();
}
Panels::CpuPanelVU::CpuPanelVU( wxWindow* parent )
@ -221,7 +221,7 @@ Panels::CpuPanelVU::CpuPanelVU( wxWindow* parent )
*this += new wxStaticLine( this ) | wxSF.Border(wxALL, 18).Expand();
*this += new AdvancedOptionsVU( this ) | StdExpand();
OnSettingsChanged();
AppStatusEvent_OnSettingsApplied();
}
void Panels::CpuPanelEE::Apply()
@ -231,7 +231,7 @@ void Panels::CpuPanelEE::Apply()
recOps.EnableIOP = !!m_panel_RecIOP->GetSelection();
}
void Panels::CpuPanelEE::OnSettingsChanged()
void Panels::CpuPanelEE::AppStatusEvent_OnSettingsApplied()
{
m_panel_RecEE->Enable( x86caps.hasStreamingSIMD2Extensions );
@ -253,7 +253,7 @@ void Panels::CpuPanelVU::Apply()
recOps.UseMicroVU1 = m_panel_VU1->GetSelection() == 1;
}
void Panels::CpuPanelVU::OnSettingsChanged()
void Panels::CpuPanelVU::AppStatusEvent_OnSettingsApplied()
{
m_panel_VU0->Enable( x86caps.hasStreamingSIMD2Extensions );
m_panel_VU1->Enable( x86caps.hasStreamingSIMD2Extensions );
@ -300,7 +300,7 @@ void Panels::AdvancedOptionsFPU::Apply()
cpuOps.ApplySanityCheck();
}
void Panels::AdvancedOptionsFPU::OnSettingsChanged()
void Panels::AdvancedOptionsFPU::AppStatusEvent_OnSettingsApplied()
{
const Pcsx2Config::CpuOptions& cpuOps( g_Conf->EmuOptions.Cpu );
const Pcsx2Config::RecompilerOptions& recOps( cpuOps.Recompiler );
@ -333,7 +333,7 @@ void Panels::AdvancedOptionsVU::Apply()
cpuOps.ApplySanityCheck();
}
void Panels::AdvancedOptionsVU::OnSettingsChanged()
void Panels::AdvancedOptionsVU::AppStatusEvent_OnSettingsApplied()
{
const Pcsx2Config::CpuOptions& cpuOps( g_Conf->EmuOptions.Cpu );
const Pcsx2Config::RecompilerOptions& recOps( cpuOps.Recompiler );

View File

@ -110,7 +110,7 @@ Panels::DirPickerPanel::DirPickerPanel( wxWindow* parent, FoldersEnum_t folderid
// wx warns when paths don't exist, but this is typically normal when the wizard
// creates its child controls. So let's ignore them.
wxDoNotLogInThisScope please;
OnSettingsChanged(); // forces default settings based on g_Conf
AppStatusEvent_OnSettingsApplied(); // forces default settings based on g_Conf
}
Panels::DirPickerPanel& Panels::DirPickerPanel::SetStaticDesc( const wxString& msg )
@ -133,7 +133,7 @@ void Panels::DirPickerPanel::Reset()
m_pickerCtrl->SetPath( GetNormalizedConfigFolder( m_FolderId ) );
}
void Panels::DirPickerPanel::OnSettingsChanged()
void Panels::DirPickerPanel::AppStatusEvent_OnSettingsApplied()
{
Reset();
}

View File

@ -105,10 +105,10 @@ Panels::GSWindowSettingsPanel::GSWindowSettingsPanel( wxWindow* parent )
*centerSizer += GetSizer() | pxCenter;
SetSizer( centerSizer, false );
OnSettingsChanged();
AppStatusEvent_OnSettingsApplied();
}
void Panels::GSWindowSettingsPanel::OnSettingsChanged()
void Panels::GSWindowSettingsPanel::AppStatusEvent_OnSettingsApplied()
{
const AppConfig::GSWindowOptions& conf( g_Conf->GSWindow );

View File

@ -76,7 +76,7 @@ Panels::GameFixesPanel::GameFixesPanel( wxWindow* parent ) :
L"will need to turn off fixes manually when changing games."
));
OnSettingsChanged();
AppStatusEvent_OnSettingsApplied();
}
// I could still probably get rid of the for loop, but I think this is clearer.
@ -92,7 +92,7 @@ void Panels::GameFixesPanel::Apply()
}
}
void Panels::GameFixesPanel::OnSettingsChanged()
void Panels::GameFixesPanel::AppStatusEvent_OnSettingsApplied()
{
const Pcsx2Config::GamefixOptions& opts( g_Conf->EmuOptions.Gamefixes );
for( int i=0; i<NUM_OF_GAME_FIXES; ++i )

View File

@ -227,10 +227,10 @@ Panels::LogOptionsPanel::LogOptionsPanel(wxWindow* parent )
Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(LogOptionsPanel::OnCheckBoxClicked) );
OnSettingsChanged();
AppStatusEvent_OnSettingsApplied();
}
void Panels::LogOptionsPanel::OnSettingsChanged()
void Panels::LogOptionsPanel::AppStatusEvent_OnSettingsApplied()
{
TraceLogFilters& conf( g_Conf->EmuOptions.Trace );

View File

@ -111,7 +111,7 @@ namespace Panels
LogOptionsPanel( wxWindow* parent );
virtual ~LogOptionsPanel() throw() {}
void OnSettingsChanged();
void AppStatusEvent_OnSettingsApplied();
void OnUpdateEnableAll();
void OnCheckBoxClicked(wxCommandEvent &event);
void Apply();

View File

@ -152,14 +152,14 @@ BaseApplicableConfigPanel::~BaseApplicableConfigPanel() throw()
BaseApplicableConfigPanel::BaseApplicableConfigPanel( wxWindow* parent, wxOrientation orient )
: wxPanelWithHelpers( parent, orient )
, m_Listener_SettingsApplied( wxGetApp().Source_SettingsApplied(), EventListener<int>( this, OnSettingsApplied ) )
, m_AppStatusHelper( this )
{
Init();
}
BaseApplicableConfigPanel::BaseApplicableConfigPanel( wxWindow* parent, wxOrientation orient, const wxString& staticLabel )
: wxPanelWithHelpers( parent, orient, staticLabel )
, m_Listener_SettingsApplied( wxGetApp().Source_SettingsApplied(), EventListener<int>( this, OnSettingsApplied ) )
, m_AppStatusHelper( this )
{
Init();
}
@ -181,12 +181,7 @@ void BaseApplicableConfigPanel::Init()
}
}
void __evt_fastcall BaseApplicableConfigPanel::OnSettingsApplied( void* obj, int& ini )
{
if( obj == NULL ) return;
((BaseApplicableConfigPanel*)obj)->OnSettingsChanged();
}
void BaseApplicableConfigPanel::AppStatusEvent_OnSettingsApplied() {}
// -----------------------------------------------------------------------
Panels::UsermodeSelectionPanel::UsermodeSelectionPanel( wxWindow* parent, bool isFirstTime )
@ -226,7 +221,7 @@ Panels::UsermodeSelectionPanel::UsermodeSelectionPanel( wxWindow* parent, bool i
*this += m_radio_UserMode | pxSizerFlags::StdExpand();
*this += 4;
OnSettingsChanged();
AppStatusEvent_OnSettingsApplied();
}
void Panels::UsermodeSelectionPanel::Apply()
@ -234,7 +229,7 @@ void Panels::UsermodeSelectionPanel::Apply()
UseAdminMode = (m_radio_UserMode->GetSelection() == 1);
}
void Panels::UsermodeSelectionPanel::OnSettingsChanged()
void Panels::UsermodeSelectionPanel::AppStatusEvent_OnSettingsApplied()
{
m_radio_UserMode->SetSelection( (int)UseAdminMode );
}
@ -267,7 +262,7 @@ Panels::LanguageSelectionPanel::LanguageSelectionPanel( wxWindow* parent )
*this += m_picker | pxSizerFlags::StdSpace();
m_picker->SetSelection( cursel );
//OnSettingsChanged();
//AppStatusEvent_OnSettingsApplied();
}
void Panels::LanguageSelectionPanel::Apply()
@ -289,7 +284,7 @@ void Panels::LanguageSelectionPanel::Apply()
}
}
void Panels::LanguageSelectionPanel::OnSettingsChanged()
void Panels::LanguageSelectionPanel::AppStatusEvent_OnSettingsApplied()
{
m_picker->SetSelection( g_Conf->LanguageId );
}

View File

@ -230,24 +230,21 @@ void Panels::PluginSelectorPanel::ComboBoxPanel::Reset()
// =====================================================================================================
// PluginSelectorPanel
// =====================================================================================================
void __evt_fastcall Panels::PluginSelectorPanel::OnCorePluginStatusChanged( void* obj, PluginEventType& evt )
void Panels::PluginSelectorPanel::DispatchEvent( const PluginEventType& evt )
{
if( obj == NULL ) return;
if( (evt != PluginsEvt_Loaded) && (evt != PluginsEvt_Unloaded) ) return; // everything else we don't care about
PluginSelectorPanel& panel = *(PluginSelectorPanel*)obj;
if( panel.IsBeingDeleted() ) return;
if( IsBeingDeleted() ) return;
const PluginInfo* pi = tbl_PluginInfo; do
{
wxComboBox& box( panel.m_ComponentBoxes->Get(pi->id) );
wxComboBox& box( m_ComponentBoxes->Get(pi->id) );
int sel = box.GetSelection();
if( sel == wxNOT_FOUND ) continue;
panel.m_ComponentBoxes->GetConfigButton(pi->id).Enable(
(panel.m_FileList==NULL || panel.m_FileList->Count() == 0) ? false :
g_Conf->FullpathMatchTest( pi->id,(*panel.m_FileList)[((int)box.GetClientData(sel))] )
m_ComponentBoxes->GetConfigButton(pi->id).Enable(
(m_FileList==NULL || m_FileList->Count() == 0) ? false :
g_Conf->FullpathMatchTest( pi->id,(*m_FileList)[((int)box.GetClientData(sel))] )
);
} while( ++pi, pi->shortname != NULL );
@ -255,7 +252,6 @@ void __evt_fastcall Panels::PluginSelectorPanel::OnCorePluginStatusChanged( void
Panels::PluginSelectorPanel::PluginSelectorPanel( wxWindow* parent, int idealWidth )
: BaseSelectorPanel( parent )
, m_Listener_CorePluginStatus( wxGetApp().Source_CorePluginStatus(), EventListener<PluginEventType> ( this, OnCorePluginStatusChanged ) )
{
if( idealWidth != wxDefaultCoord ) m_idealWidth = idealWidth;
@ -282,7 +278,7 @@ Panels::PluginSelectorPanel::PluginSelectorPanel( wxWindow* parent, int idealWid
Connect( ButtonId_Configure, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PluginSelectorPanel::OnConfigure_Clicked ) );
OnSettingsChanged();
AppStatusEvent_OnSettingsApplied();
}
Panels::PluginSelectorPanel::~PluginSelectorPanel() throw()
@ -290,7 +286,7 @@ Panels::PluginSelectorPanel::~PluginSelectorPanel() throw()
CancelRefresh(); // in case the enumeration thread is currently refreshing...
}
void Panels::PluginSelectorPanel::OnSettingsChanged()
void Panels::PluginSelectorPanel::AppStatusEvent_OnSettingsApplied()
{
m_ComponentBoxes->GetDirPicker().Reset();
}

View File

@ -265,7 +265,7 @@ Panels::SpeedHacksPanel::SpeedHacksPanel( wxWindow* parent )
Connect( m_check_Enable->GetId(), wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( SpeedHacksPanel::OnEnable_Toggled ) );
Connect( wxID_DEFAULT, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SpeedHacksPanel::Defaults_Click ) );
OnSettingsChanged();
AppStatusEvent_OnSettingsApplied();
}
void Panels::SpeedHacksPanel::EnableStuff()
@ -282,12 +282,12 @@ void Panels::SpeedHacksPanel::EnableStuff()
}
}
void Panels::SpeedHacksPanel::OnSettingsChanged()
void Panels::SpeedHacksPanel::AppStatusEvent_OnSettingsApplied()
{
OnSettingsChanged( g_Conf->EmuOptions.Speedhacks );
AppStatusEvent_OnSettingsApplied( g_Conf->EmuOptions.Speedhacks );
}
void Panels::SpeedHacksPanel::OnSettingsChanged( const Pcsx2Config::SpeedhackOptions& opts )
void Panels::SpeedHacksPanel::AppStatusEvent_OnSettingsApplied( const Pcsx2Config::SpeedhackOptions& opts )
{
const bool enabled = g_Conf->EnableSpeedHacks;
@ -335,7 +335,7 @@ void Panels::SpeedHacksPanel::OnEnable_Toggled( wxCommandEvent& evt )
void Panels::SpeedHacksPanel::Defaults_Click( wxCommandEvent& evt )
{
OnSettingsChanged( Pcsx2Config::SpeedhackOptions() );
AppStatusEvent_OnSettingsApplied( Pcsx2Config::SpeedhackOptions() );
evt.Skip();
}

View File

@ -105,10 +105,10 @@ Panels::FramelimiterPanel::FramelimiterPanel( wxWindow* parent )
L"percentages of the default region-based framerate, which can also be configured below." )
);
OnSettingsChanged();
AppStatusEvent_OnSettingsApplied();
}
void Panels::FramelimiterPanel::OnSettingsChanged()
void Panels::FramelimiterPanel::AppStatusEvent_OnSettingsApplied()
{
const AppConfig::GSWindowOptions& appwin( g_Conf->GSWindow );
const AppConfig::FramerateOptions& appfps( g_Conf->Framerate );
@ -226,10 +226,10 @@ Panels::FrameSkipPanel::FrameSkipPanel( wxWindow* parent )
L"Enabling it will cause severe graphical errors in some games, and so it should be considered a speedhack." )
);
OnSettingsChanged();
AppStatusEvent_OnSettingsApplied();
}
void Panels::FrameSkipPanel::OnSettingsChanged()
void Panels::FrameSkipPanel::AppStatusEvent_OnSettingsApplied()
{
const AppConfig::FramerateOptions& appfps( g_Conf->Framerate );
const Pcsx2Config::GSOptions& gsconf( g_Conf->EmuOptions.GS );
@ -332,7 +332,7 @@ Panels::VideoPanel::VideoPanel( wxWindow* parent ) :
Connect( m_button_OpenWindowSettings->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(VideoPanel::OnOpenWindowSettings) );
OnSettingsChanged();
AppStatusEvent_OnSettingsApplied();
}
void Panels::VideoPanel::OnOpenWindowSettings( wxCommandEvent& evt )
@ -348,7 +348,7 @@ void Panels::VideoPanel::Apply()
g_Conf->EmuOptions.GS.DisableOutput = m_check_DisableOutput->GetValue();
}
void Panels::VideoPanel::OnSettingsChanged()
void Panels::VideoPanel::AppStatusEvent_OnSettingsApplied()
{
m_check_SynchronousGS->SetValue( g_Conf->EmuOptions.GS.SynchronousMTGS );
m_check_DisableOutput->SetValue( g_Conf->EmuOptions.GS.DisableOutput );

View File

@ -29,6 +29,10 @@ using namespace Threading;
static FnType_OnThreadComplete* Callback_PluginsLoadComplete = NULL;
// The GS plugin needs to be opened to save/load the state during plugin configuration, but
// the window shouldn't. This blocks it. :)
static bool s_DisableGsWindow = false;
// --------------------------------------------------------------------------------------
// AppPluginManager
// --------------------------------------------------------------------------------------
@ -89,7 +93,7 @@ public:
// Yay, this plugin is guaranteed to always be opened first and closed last.
bool OpenPlugin_GS()
{
if( GSopen2 != NULL )
if( GSopen2 != NULL && !s_DisableGsWindow )
{
sApp.OpenGsPanel();
}
@ -187,6 +191,8 @@ void LoadPluginsTask::OnCleanupInThread()
SaveSinglePluginHelper::SaveSinglePluginHelper( PluginsEnum_t pid )
: m_plugstore( L"PluginConf Savestate" )
{
s_DisableGsWindow = true;
m_whereitsat = NULL;
m_resume = false;
m_pid = pid;
@ -210,14 +216,24 @@ SaveSinglePluginHelper::SaveSinglePluginHelper( PluginsEnum_t pid )
SaveSinglePluginHelper::~SaveSinglePluginHelper() throw()
{
if( m_validstate )
try
{
Console.WriteLn( Color_Green, L"Recovering single plugin: " + tbl_PluginInfo[m_pid].GetShortname() );
memLoadingState load( *m_whereitsat );
if( m_plugstore.IsDisposed() ) load.SeekToSection( m_pid );
g_plugins->Freeze( m_pid, load );
if( m_validstate )
{
Console.WriteLn( Color_Green, L"Recovering single plugin: " + tbl_PluginInfo[m_pid].GetShortname() );
memLoadingState load( *m_whereitsat );
if( m_plugstore.IsDisposed() ) load.SeekToSection( m_pid );
g_plugins->Freeze( m_pid, load );
g_plugins->Close( m_pid );
}
}
catch( Exception::BaseException& ex )
{
s_DisableGsWindow = false;
throw;
}
s_DisableGsWindow = false;
if( m_resume ) CoreThread.Resume();
}
@ -256,15 +272,14 @@ void ConvertPluginFilenames( wxString (&passins)[PluginId_Count] )
}
// boolean lock modified from the main thread only...
static bool plugin_load_lock = false;
static LoadPluginsTask* plugin_load_lock = NULL;
void Pcsx2App::ReloadPlugins()
{
if( SelfMethodInvoke( &Pcsx2App::ReloadPlugins ) ) return;
if( InvokeMethodOnMainThread( &Pcsx2App::ReloadPlugins ) ) return;
if( plugin_load_lock ) return;
CoreThread.Cancel();
m_CorePlugins = NULL;
UnloadPlugins();
wxString passins[PluginId_Count];
@ -276,27 +291,29 @@ void Pcsx2App::ReloadPlugins()
passins[pi->id] = g_Conf->FullpathTo( pi->id );
} while( ++pi, pi->shortname != NULL );
(new LoadPluginsTask( passins ))->Start();
// ... and when it finishes it posts up a OnLoadPluginsComplete(). Bye. :)
plugin_load_lock = new LoadPluginsTask(passins);
plugin_load_lock->Start();
plugin_load_lock = true;
// ... and when it finishes it posts up a OnLoadPluginsComplete(). Bye. :)
}
// Note: If the ClientData paremeter of wxCommandEvt is NULL, this message simply dispatches
// Note: If the ClientData parameter of wxCommandEvt is NULL, this message simply dispatches
// the plugged in listeners.
void Pcsx2App::OnLoadPluginsComplete( wxCommandEvent& evt )
{
plugin_load_lock = false;
FnType_OnThreadComplete* fn_tmp = Callback_PluginsLoadComplete;
if( evt.GetClientData() != NULL )
if( LoadPluginsTask* pluginthread = (LoadPluginsTask*)evt.GetClientData() )
{
// scoped ptr ensures the thread object is cleaned up even on exception:
ScopedPtr<LoadPluginsTask> killTask( pluginthread );
pxAssume( plugin_load_lock == pluginthread );
plugin_load_lock = NULL;
if( !pxAssertDev( !m_CorePlugins, "LoadPlugins thread just finished, but CorePlugins state != NULL (odd!)." ) )
m_CorePlugins = NULL;
// scoped ptr ensures the thread object is cleaned up even on exception:
ScopedPtr<LoadPluginsTask> killTask( (LoadPluginsTask*)evt.GetClientData() );
killTask->RethrowException();
m_CorePlugins = killTask->Result;
}
@ -306,6 +323,12 @@ void Pcsx2App::OnLoadPluginsComplete( wxCommandEvent& evt )
PostPluginStatus( PluginsEvt_Loaded );
}
void Pcsx2App::CancelLoadingPlugins()
{
if( plugin_load_lock )
plugin_load_lock->Cancel();
}
void Pcsx2App::PostPluginStatus( PluginEventType pevt )
{
if( !wxThread::IsMain() )
@ -314,7 +337,7 @@ void Pcsx2App::PostPluginStatus( PluginEventType pevt )
}
else
{
sApp.Source_CorePluginStatus().Dispatch( pevt );
sApp.DispatchEvent( pevt );
}
}

View File

@ -15,7 +15,7 @@
#include "PrecompiledHeader.h"
#include "MainFrame.h"
#include "IniInterface.h"
// FIXME : This needs to handle removed/missing ISOs somehow, although I'm not sure the
// best approach. I think I'd prefer for missing entries to only be removed when they
@ -27,8 +27,6 @@
RecentIsoManager::RecentIsoManager( wxMenu* menu )
: m_Menu( menu )
, m_MaxLength( g_Conf->RecentIsoCount )
, m_Listener_SettingsLoadSave ( wxGetApp().Source_SettingsLoadSave(), EventListener<IniInterface>( this, OnSettingsLoadSave ) )
, m_Listener_SettingsApplied ( wxGetApp().Source_SettingsApplied(), EventListener<int>( this, OnSettingsApplied ) )
{
m_cursel = 0;
m_Separator = NULL;
@ -139,13 +137,15 @@ void RecentIsoManager::InsertIntoMenu( int id )
curitem.ItemPtr->Check();
}
void RecentIsoManager::DoSettingsApplied( int& ini )
void RecentIsoManager::AppEvent_OnSettingsApplied()
{
// TODO : Implement application of Recent Iso List "maximum" history option
}
void RecentIsoManager::DoSettingsLoadSave( IniInterface& ini )
void RecentIsoManager::AppEvent_OnSettingsLoadSave( const AppSettingsEventInfo& evt )
{
IniInterface& ini( evt.GetIni() );
ini.GetConfig().SetRecordDefaults( false );
if( ini.IsSaving() )
@ -179,15 +179,3 @@ void RecentIsoManager::DoSettingsLoadSave( IniInterface& ini )
ini.GetConfig().SetRecordDefaults( true );
}
void __evt_fastcall RecentIsoManager::OnSettingsLoadSave( void* obj, IniInterface& ini )
{
if( obj == NULL ) return;
((RecentIsoManager*)obj)->DoSettingsLoadSave( ini );
}
void __evt_fastcall RecentIsoManager::OnSettingsApplied( void* obj, int& ini )
{
if( obj == NULL ) return;
((RecentIsoManager*)obj)->DoSettingsApplied( ini );
}

View File

@ -18,7 +18,8 @@
// --------------------------------------------------------------------------------------
// RecentIsoManager
// --------------------------------------------------------------------------------------
class RecentIsoManager : public wxEvtHandler
class RecentIsoManager : public wxEvtHandler,
public IEventListener_AppStatus
{
protected:
struct RecentItem
@ -43,9 +44,6 @@ protected:
wxMenuItem* m_Separator;
EventListenerBinding<IniInterface> m_Listener_SettingsLoadSave;
EventListenerBinding<int> m_Listener_SettingsApplied;
public:
RecentIsoManager( wxMenu* menu );
virtual ~RecentIsoManager() throw();
@ -56,11 +54,9 @@ public:
protected:
void InsertIntoMenu( int id );
void DoSettingsLoadSave( IniInterface& ini );
void DoSettingsApplied( int& val );
void OnChangedSelection( wxCommandEvent& evt );
static void __evt_fastcall OnSettingsLoadSave( void* obj, IniInterface& evt );
static void __evt_fastcall OnSettingsApplied( void* obj, int& evt );
void AppEvent_OnSettingsLoadSave( const AppSettingsEventInfo& ini );
void AppEvent_OnSettingsApplied();
};

View File

@ -21,29 +21,20 @@
# include <wx/msw/wrapwin.h> // needed for windows-specific rich text messages to make scrolling not lame
#endif
void __evt_fastcall pxLogTextCtrl::OnCoreThreadStatusChanged( void* obj, wxCommandEvent& evt )
void pxLogTextCtrl::DispatchEvent( const CoreThreadStatus& status )
{
#ifdef __WXMSW__
if( obj == NULL ) return;
pxLogTextCtrl* mframe = (pxLogTextCtrl*)obj;
// See ConcludeIssue for details on WM_VSCROLL
if( mframe->HasWriteLock() ) return;
::SendMessage((HWND)mframe->GetHWND(), WM_VSCROLL, SB_BOTTOM, (LPARAM)NULL);
if( HasWriteLock() ) return;
::SendMessage((HWND)GetHWND(), WM_VSCROLL, SB_BOTTOM, (LPARAM)NULL);
#endif
}
void __evt_fastcall pxLogTextCtrl::OnCorePluginStatusChanged( void* obj, PluginEventType& evt )
void pxLogTextCtrl::DispatchEvent( const PluginEventType& evt )
{
#ifdef __WXMSW__
if( obj == NULL ) return;
pxLogTextCtrl* mframe = (pxLogTextCtrl*)obj;
// See ConcludeIssue for details on WM_VSCROLL
if( mframe->HasWriteLock() ) return;
::SendMessage((HWND)mframe->GetHWND(), WM_VSCROLL, SB_BOTTOM, (LPARAM)NULL);
if( HasWriteLock() ) return;
::SendMessage((HWND)GetHWND(), WM_VSCROLL, SB_BOTTOM, (LPARAM)NULL);
#endif
}
@ -51,9 +42,6 @@ pxLogTextCtrl::pxLogTextCtrl( wxWindow* parent )
: wxTextCtrl( parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH2
)
, m_Listener_CoreThreadStatus ( wxGetApp().Source_CoreThreadStatus(), CmdEvt_Listener ( this, OnCoreThreadStatusChanged ) )
, m_Listener_CorePluginStatus ( wxGetApp().Source_CorePluginStatus(), EventListener<PluginEventType> ( this, OnCorePluginStatusChanged ) )
{
#ifdef __WXMSW__
m_win32_LinesPerScroll = 10;

View File

@ -1820,6 +1820,10 @@
RelativePath="..\..\gui\AppCoreThread.cpp"
>
</File>
<File
RelativePath="..\..\gui\AppEventSources.cpp"
>
</File>
<File
RelativePath="..\..\gui\AppInit.cpp"
>
@ -1896,10 +1900,6 @@
RelativePath="..\..\gui\Saveslots.cpp"
>
</File>
<File
RelativePath="..\..\gui\wxAppWithHelpers.cpp"
>
</File>
<Filter
Name="Dialogs"
>
@ -1967,6 +1967,10 @@
RelativePath="..\..\gui\Dialogs\PickUserModeDialog.cpp"
>
</File>
<File
RelativePath="..\..\gui\Dialogs\StuckThreadDialog.cpp"
>
</File>
<File
RelativePath="..\..\gui\Dialogs\SysConfigDialog.cpp"
>
@ -2535,6 +2539,14 @@
RelativePath="..\..\gui\AppConfig.h"
>
</File>
<File
RelativePath="..\..\gui\AppEventListeners.h"
>
</File>
<File
RelativePath="..\..\gui\AppForwardDefs.h"
>
</File>
<File
RelativePath="..\..\gui\ApplyState.h"
>
@ -2567,10 +2579,6 @@
RelativePath="..\..\gui\RecentIsoList.h"
>
</File>
<File
RelativePath="..\..\gui\wxAppWithHelpers.h"
>
</File>
</Filter>
<Filter
Name="Win32"

View File

@ -25,13 +25,8 @@ int SysPageFaultExceptionFilter( EXCEPTION_POINTERS* eps )
if( eps->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION )
return EXCEPTION_CONTINUE_SEARCH;
PageFaultInfo info( (uptr)eps->ExceptionRecord->ExceptionInformation[1] );
Source_PageFault.DispatchException( info );
if( info.handled ) return EXCEPTION_CONTINUE_EXECUTION;
return EXCEPTION_CONTINUE_SEARCH;
Source_PageFault.Dispatch( PageFaultInfo( (uptr)eps->ExceptionRecord->ExceptionInformation[1] ) );
return Source_PageFault.WasHandled() ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH;
}
void InstallSignalHandler()

View File

@ -729,8 +729,8 @@ Global
{6BC4D85D-A399-407E-96A9-CD5416A54269}.Release|Win32.ActiveCfg = Release|Win32
{6BC4D85D-A399-407E-96A9-CD5416A54269}.Release|Win32.Build.0 = Release|Win32
{6BC4D85D-A399-407E-96A9-CD5416A54269}.Release|x64.ActiveCfg = Release|Win32
{0A18A071-125E-442F-AFF7-A3F68ABECF99}.Debug|Win32.ActiveCfg = Debug|Win32
{0A18A071-125E-442F-AFF7-A3F68ABECF99}.Debug|Win32.Build.0 = Debug|Win32
{0A18A071-125E-442F-AFF7-A3F68ABECF99}.Debug|Win32.ActiveCfg = Debug (NO ASIO)|Win32
{0A18A071-125E-442F-AFF7-A3F68ABECF99}.Debug|Win32.Build.0 = Debug (NO ASIO)|Win32
{0A18A071-125E-442F-AFF7-A3F68ABECF99}.Debug|x64.ActiveCfg = Debug|x64
{0A18A071-125E-442F-AFF7-A3F68ABECF99}.Debug|x64.Build.0 = Debug|x64
{0A18A071-125E-442F-AFF7-A3F68ABECF99}.Devel|Win32.ActiveCfg = Release (NO ASIO)|Win32

View File

@ -257,20 +257,20 @@ public:
CfgReadStr( L"PORTAUDIO", L"Device", m_Device, 254, L"default" );
m_ApiId = -1;
if(api == L"InDevelopment") m_ApiId = paInDevelopment; /* use while developing support for a new host API */
if(api == L"DirectSound") m_ApiId = paDirectSound;
if(api == L"MME") m_ApiId = paMME;
if(api == L"ASIO") m_ApiId = paASIO;
if(api == L"SoundManager") m_ApiId = paSoundManager;
if(api == L"CoreAudio") m_ApiId = paCoreAudio;
if(api == L"OSS") m_ApiId = paOSS;
if(api == L"ALSA") m_ApiId = paALSA;
if(api == L"AL") m_ApiId = paAL;
if(api == L"BeOS") m_ApiId = paBeOS;
if(api == L"WDMKS") m_ApiId = paWDMKS;
if(api == L"JACK") m_ApiId = paJACK;
if(api == L"WASAPI") m_ApiId = paWASAPI;
if(api == L"AudioScienceHPI") m_ApiId = paAudioScienceHPI;
if(api == L"InDevelopment") m_ApiId = paInDevelopment; /* use while developing support for a new host API */
if(api == L"DirectSound") m_ApiId = paDirectSound;
if(api == L"MME") m_ApiId = paMME;
if(api == L"ASIO") m_ApiId = paASIO;
if(api == L"SoundManager") m_ApiId = paSoundManager;
if(api == L"CoreAudio") m_ApiId = paCoreAudio;
if(api == L"OSS") m_ApiId = paOSS;
if(api == L"ALSA") m_ApiId = paALSA;
if(api == L"AL") m_ApiId = paAL;
if(api == L"BeOS") m_ApiId = paBeOS;
if(api == L"WDMKS") m_ApiId = paWDMKS;
if(api == L"JACK") m_ApiId = paJACK;
if(api == L"WASAPI") m_ApiId = paWASAPI;
if(api == L"AudioScienceHPI") m_ApiId = paAudioScienceHPI;
}
@ -279,21 +279,21 @@ public:
wstring api;
switch(m_ApiId)
{
case paInDevelopment: api = L"InDevelopment"; break; /* use while developing support for a new host API */
case paDirectSound: api = L"DirectSound"; break;
case paMME: api = L"MME"; break;
case paASIO: api = L"ASIO"; break;
case paSoundManager: api = L"SoundManager"; break;
case paCoreAudio: api = L"CoreAudio"; break;
case paOSS: api = L"OSS"; break;
case paALSA: api = L"ALSA"; break;
case paAL: api = L"AL"; break;
case paBeOS: api = L"BeOS"; break;
case paWDMKS: api = L"WDMKS"; break;
case paJACK: api = L"JACK"; break;
case paWASAPI: api = L"WASAPI"; break;
case paAudioScienceHPI: api = L"AudioScienceHPI"; break;
default: api = L"Unknown";
case paInDevelopment: api = L"InDevelopment"; break; /* use while developing support for a new host API */
case paDirectSound: api = L"DirectSound"; break;
case paMME: api = L"MME"; break;
case paASIO: api = L"ASIO"; break;
case paSoundManager: api = L"SoundManager"; break;
case paCoreAudio: api = L"CoreAudio"; break;
case paOSS: api = L"OSS"; break;
case paALSA: api = L"ALSA"; break;
case paAL: api = L"AL"; break;
case paBeOS: api = L"BeOS"; break;
case paWDMKS: api = L"WDMKS"; break;
case paJACK: api = L"JACK"; break;
case paWASAPI: api = L"WASAPI"; break;
case paAudioScienceHPI: api = L"AudioScienceHPI"; break;
default: api = L"Unknown";
}
CfgWriteStr( L"PORTAUDIO", L"HostApi", api);