Major UI renovations!

* Added versioning info to savestates! (along with some other bugfixes)
 * Simplified the Boot and System menus -- removed the old Skip BIOS hack and replaced it with the new BOOT2 injection method (which is considered hack-free at this time).
 * Removed lots of UI deadlock gotchas.
 * Some new confirmation dialogs and better error handling.
 * Implemented an exclusive SysExecutor thread, which serves the purpose of executing system/VM commands and events in uninterrupted order (including suspend, resume, savestates, etc.)



 * ... and probably broke linux!

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2911 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2010-04-27 13:12:03 +00:00
parent c440cb563a
commit 5d37925617
89 changed files with 5095 additions and 3156 deletions

View File

@ -156,12 +156,14 @@
<Unit filename="../../include/Utilities/Path.h" />
<Unit filename="../../include/Utilities/PersistentThread.h" />
<Unit filename="../../include/Utilities/RedtapeWindows.h" />
<Unit filename="../../include/Utilities/RwMutex.h" />
<Unit filename="../../include/Utilities/SafeArray.h" />
<Unit filename="../../include/Utilities/ScopedPtr.h" />
<Unit filename="../../include/Utilities/StringHelpers.h" />
<Unit filename="../../include/Utilities/Threading.h" />
<Unit filename="../../include/Utilities/lnx_memzero.h" />
<Unit filename="../../include/Utilities/pxCheckBox.h" />
<Unit filename="../../include/Utilities/pxEvents.h" />
<Unit filename="../../include/Utilities/pxRadioPanel.h" />
<Unit filename="../../include/Utilities/pxStaticText.h" />
<Unit filename="../../include/Utilities/win_memzero.h" />
@ -182,6 +184,7 @@
<Unit filename="../../src/Utilities/PathUtils.cpp" />
<Unit filename="../../src/Utilities/PrecompiledHeader.cpp" />
<Unit filename="../../src/Utilities/PrecompiledHeader.h" />
<Unit filename="../../src/Utilities/RwMutex.cpp" />
<Unit filename="../../src/Utilities/Semaphore.cpp" />
<Unit filename="../../src/Utilities/StringHelpers.cpp" />
<Unit filename="../../src/Utilities/ThreadTools.cpp" />

View File

@ -207,10 +207,6 @@
RelativePath="..\..\src\Utilities\Console.cpp"
>
</File>
<File
RelativePath="..\..\src\Utilities\EventSource.cpp"
>
</File>
<File
RelativePath="..\..\src\Utilities\Exceptions.cpp"
>
@ -426,6 +422,10 @@
RelativePath="..\..\src\Utilities\Mutex.cpp"
>
</File>
<File
RelativePath="..\..\src\Utilities\RwMutex.cpp"
>
</File>
<File
RelativePath="..\..\src\Utilities\Semaphore.cpp"
>
@ -501,10 +501,6 @@
RelativePath="..\..\include\Utilities\Path.h"
>
</File>
<File
RelativePath="..\..\include\Utilities\PersistentThread.h"
>
</File>
<File
RelativePath="..\..\src\Utilities\PrecompiledHeader.h"
>
@ -513,6 +509,10 @@
RelativePath="..\..\include\Utilities\pxCheckBox.h"
>
</File>
<File
RelativePath="..\..\include\Utilities\pxEvents.h"
>
</File>
<File
RelativePath="..\..\include\Utilities\pxRadioPanel.h"
>
@ -556,6 +556,14 @@
<Filter
Name="Threading"
>
<File
RelativePath="..\..\include\Utilities\PersistentThread.h"
>
</File>
<File
RelativePath="..\..\include\Utilities\RwMutex.h"
>
</File>
<File
RelativePath="..\..\include\Utilities\Threading.h"
>

View File

@ -154,8 +154,14 @@ public:
}
virtual ~ConsoleIndentScope() throw()
{
if( m_amount != 0 ) Console.SetIndent( -m_amount );
}
void EndScope()
{
Console.SetIndent( -m_amount );
m_amount = 0;
}
};

View File

@ -15,6 +15,8 @@
#pragma once
#include "Threading.h"
using Threading::ScopedLock;
template< typename ListenerType >
@ -77,7 +79,7 @@ __forceinline void EventSource<ListenerType>::_DispatchRaw( ListenerIterator ite
Console.Error( L"Ignoring runtime error thrown from event listener: " + ex.FormatDiagnosticMessage() );
}
}
catch( Exception::BaseException& ex )
catch( BaseException& ex )
{
if( IsDevBuild )
{

View File

@ -27,7 +27,7 @@
// friendly error log in their wake.
//
#define __DESTRUCTOR_CATCHALL( funcname ) \
catch( Exception::BaseException& ex ) \
catch( BaseException& ex ) \
{ \
Console.Error( "Unhandled BaseException in %s (ignored!):", funcname ); \
Console.Error( ex.FormatDiagnosticMessage() ); \
@ -155,6 +155,8 @@ namespace Exception
bool IsSilent;
public:
DEFINE_RUNTIME_EXCEPTION( RuntimeError, wxLt("An unhandled runtime error has occurred, somewhere in the depths of Pcsx2's cluttered brain-matter.") )
RuntimeError( const std::runtime_error& ex, const wxString& prefix=wxEmptyString );
};
// --------------------------------------------------------------------------------------
@ -243,7 +245,7 @@ namespace Exception
// ---------------------------------------------------------------------------------------
// Streaming (file) Exceptions:
// Stream / BadStream / CreateStream / FileNotFound / AccessDenied / EndOfStream
// Stream / BadStream / CannotCreateStream / FileNotFound / AccessDenied / EndOfStream
// ---------------------------------------------------------------------------------------
#define DEFINE_STREAM_EXCEPTION( classname, defmsg ) \
@ -308,22 +310,22 @@ namespace Exception
// A generic exception for odd-ball stream creation errors.
//
class CreateStream : public virtual Stream
class CannotCreateStream : public virtual Stream
{
public:
DEFINE_STREAM_EXCEPTION( CreateStream, wxLt("File could not be created or opened.") )
DEFINE_STREAM_EXCEPTION( CannotCreateStream, wxLt("File could not be created or opened.") )
};
// Exception thrown when an attempt to open a non-existent file is made.
// (this exception can also mean file permissions are invalid)
//
class FileNotFound : public virtual CreateStream
class FileNotFound : public virtual CannotCreateStream
{
public:
DEFINE_STREAM_EXCEPTION( FileNotFound, wxLt("File not found.") )
};
class AccessDenied : public virtual CreateStream
class AccessDenied : public virtual CannotCreateStream
{
public:
DEFINE_STREAM_EXCEPTION( AccessDenied, wxLt("Permission denied to file.") )
@ -358,3 +360,5 @@ namespace Exception
};
#endif
}
using Exception::BaseException;

View File

@ -53,6 +53,9 @@ public:
bool IsReentrant() const { return Counter > 1; }
};
// --------------------------------------------------------------------------------------
// ICloneable / IActionInvocation / IDeletableObject
// --------------------------------------------------------------------------------------
class IActionInvocation
{
public:
@ -60,8 +63,30 @@ public:
virtual void InvokeAction()=0;
};
class ICloneable
{
public:
virtual ICloneable* Clone() const=0;
};
class IDeletableObject
{
public:
virtual ~IDeletableObject() throw() {}
virtual void DeleteSelf()=0;
virtual bool IsBeingDeleted()=0;
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()=0;
};
// --------------------------------------------------------------------------------------
// IDeletableObject
// BaseDeletableObject
// --------------------------------------------------------------------------------------
// 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
@ -83,14 +108,14 @@ public:
// (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
class BaseDeletableObject : public virtual IDeletableObject
{
protected:
volatile long m_IsBeingDeleted;
public:
IDeletableObject();
virtual ~IDeletableObject() throw();
BaseDeletableObject();
virtual ~BaseDeletableObject() throw();
void DeleteSelf();
bool IsBeingDeleted() { return !!m_IsBeingDeleted; }

View File

@ -60,6 +60,7 @@ public:
bool IsWritable() const { return IsDirWritable(); }
bool IsReadable() const { return IsDirReadable(); }
bool Exists() const { return DirExists(); }
bool FileExists() const { return wxFileName::FileExists(); }
bool IsOk() const { return wxFileName::IsOk(); }
bool IsRelative() const { return wxFileName::IsRelative(); }
bool IsAbsolute() const { return wxFileName::IsAbsolute(); }

View File

@ -123,14 +123,14 @@ namespace Threading
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.
MutexRecursive 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;
ScopedPtr<BaseException> m_except;
EventSource<EventListener_Thread> m_evtsrc_OnDelete;
@ -148,6 +148,7 @@ namespace Threading
virtual bool Cancel( const wxTimeSpan& timeout );
virtual bool Detach();
virtual void Block();
virtual bool Block( const wxTimeSpan& timeout );
virtual void RethrowException() const;
void AddListener( EventListener_Thread& evt );

View File

@ -0,0 +1,90 @@
/* 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"
namespace Threading
{
// --------------------------------------------------------------------------------------
// RwMutex
// --------------------------------------------------------------------------------------
class RwMutex
{
DeclareNoncopyableObject(RwMutex);
protected:
pthread_rwlock_t m_rwlock;
public:
RwMutex();
virtual ~RwMutex() throw();
virtual void AcquireRead();
virtual void AcquireWrite();
virtual bool TryAcquireRead();
virtual bool TryAcquireWrite();
virtual void Release();
};
// --------------------------------------------------------------------------------------
// BaseScopedReadWriteLock
// --------------------------------------------------------------------------------------
class BaseScopedReadWriteLock
{
DeclareNoncopyableObject(BaseScopedReadWriteLock);
protected:
RwMutex& m_lock;
bool m_IsLocked;
public:
BaseScopedReadWriteLock( RwMutex& locker )
: m_lock( locker )
{
}
virtual ~BaseScopedReadWriteLock() throw();
void Release();
bool IsLocked() const { return m_IsLocked; }
};
// --------------------------------------------------------------------------------------
// ScopedReadLock / ScopedWriteLock
// --------------------------------------------------------------------------------------
class ScopedReadLock : public BaseScopedReadWriteLock
{
public:
ScopedReadLock( RwMutex& locker );
virtual ~ScopedReadLock() throw() {}
void Acquire();
};
class ScopedWriteLock : public BaseScopedReadWriteLock
{
public:
ScopedWriteLock( RwMutex& locker );
virtual ~ScopedWriteLock() throw() {}
void Acquire();
protected:
ScopedWriteLock( RwMutex& locker, bool isTryLock );
};
}

View File

@ -24,7 +24,7 @@
#undef Yield // release the burden of windows.h global namespace spam.
#define AffinityAssert_AllowFromMain() \
#define AffinityAssert_AllowFrom_MainUI() \
pxAssertMsg( wxThread::IsMain(), "Thread affinity violation: Call allowed from main thread only." )
// --------------------------------------------------------------------------------------
@ -48,7 +48,9 @@ class wxTimeSpan;
namespace Threading
{
class PersistentThread;
class RwMutex;
extern void pxTestCancel();
extern PersistentThread* pxGetCurrentThread();
extern wxString pxGetCurrentThreadName();
extern u64 GetThreadCpuTime();
@ -110,43 +112,6 @@ namespace Exception
BaseException::InitBaseEx( msg_diag, msg_user );
}
};
#if wxUSE_GUI
// --------------------------------------------------------------------------------------
// 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)
// is reached before the lock becomes available. This exception cannot occur in the following
// conditions:
// * 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 ThreadDeadlock : public virtual BaseThreadError
{
public:
DEFINE_EXCEPTION_COPYTORS( ThreadDeadlock )
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 );
}
ThreadDeadlock( Threading::PersistentThread& _thread, const char* msg="Blocking action timed out waiting for '%s' (potential thread deadlock)." )
{
m_thread = &_thread;
BaseException::InitBaseEx( msg );
}
ThreadDeadlock( Threading::PersistentThread& _thread, const wxString& msg_diag, const wxString& msg_user )
{
m_thread = &_thread;
BaseException::InitBaseEx( msg_diag, msg_user );
}
};
#endif
}
@ -298,17 +263,19 @@ namespace Threading
void Wait();
bool Wait( const wxTimeSpan& timeout );
void WaitWithoutYield();
bool WaitWithoutYield( const wxTimeSpan& timeout );
protected:
// empty constructor used by MutexLockRecursive
Mutex( bool ) {}
};
class MutexLockRecursive : public Mutex
class MutexRecursive : public Mutex
{
public:
MutexLockRecursive();
virtual ~MutexLockRecursive() throw();
MutexRecursive();
virtual ~MutexRecursive() throw();
virtual bool IsRecursive() const { return true; }
};
@ -319,60 +286,46 @@ namespace Threading
// generally clean) method of locking code inside a function or conditional block. The lock
// will be automatically released on any return or exit from the function.
//
// Const qualification note:
// ScopedLock takes const instances of the mutex, even though the mutex is modified
// by locking and unlocking. Two rationales:
//
// 1) when designing classes with accessors (GetString, GetValue, etc) that need mutexes,
// this class needs a const hack to allow those accessors to be const (which is typically
// *very* important).
//
// 2) The state of the Mutex is guaranteed to be unchanged when the calling function or
// scope exits, by any means. Only via manual calls to Release or Acquire does that
// change, and typically those are only used in very special circumstances of their own.
//
class ScopedLock
{
DeclareNoncopyableObject(ScopedLock);
protected:
Mutex& m_lock;
Mutex* m_lock;
bool m_IsLocked;
public:
virtual ~ScopedLock() throw()
{
if( m_IsLocked )
m_lock.Release();
}
ScopedLock( Mutex& locker ) :
m_lock( locker )
{
m_IsLocked = true;
m_lock.Acquire();
}
// Provides manual unlocking of a scoped lock prior to object destruction.
void Release()
{
if( !m_IsLocked ) return;
m_IsLocked = false;
m_lock.Release();
}
// provides manual locking of a scoped lock, to re-lock after a manual unlocking.
void Acquire()
{
if( m_IsLocked ) return;
m_lock.Acquire();
m_IsLocked = true;
}
virtual ~ScopedLock() throw();
explicit ScopedLock( const Mutex* locker=NULL );
explicit ScopedLock( const Mutex& locker );
void AssignAndLock( const Mutex& locker );
void AssignAndLock( const Mutex* locker );
void Release();
void Acquire();
bool IsLocked() const { return m_IsLocked; }
protected:
// Special constructor used by ScopedTryLock
ScopedLock( Mutex& locker, bool isTryLock ) :
m_lock( locker )
{
m_IsLocked = isTryLock ? m_lock.TryAcquire() : false;
}
ScopedLock( const Mutex& locker, bool isTryLock );
};
class ScopedTryLock : public ScopedLock
{
public:
ScopedTryLock( Mutex& locker ) : ScopedLock( locker, true ) { }
ScopedTryLock( const Mutex& locker ) : ScopedLock( locker, true ) { }
virtual ~ScopedTryLock() throw() {}
bool Failed() const { return !m_IsLocked; }
};

View File

@ -19,109 +19,12 @@
#include "Threading.h"
#include "wxGuiTools.h"
#include "pxEvents.h"
using namespace Threading;
class pxPingEvent;
class pxMessageBoxEvent;
class pxSynchronousCommandEvent;
BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE( pxEvt_Ping, -1 )
DECLARE_EVENT_TYPE( pxEvt_IdleEventQueue, -1 )
DECLARE_EVENT_TYPE( pxEvt_MessageBox, -1 )
DECLARE_EVENT_TYPE( pxEvt_DeleteObject, -1 )
//DECLARE_EVENT_TYPE( pxEvt_Assertion, -1 )
END_DECLARE_EVENT_TYPES()
struct MsgboxEventResult
{
Semaphore WaitForMe;
int result;
MsgboxEventResult()
{
result = 0;
}
};
// --------------------------------------------------------------------------------------
// MsgButtons
// --------------------------------------------------------------------------------------
class MsgButtons
{
protected:
BITFIELD32()
bool
m_OK :1,
m_Cancel :1,
m_Yes :1,
m_No :1,
m_AllowToAll:1,
m_Apply :1,
m_Abort :1,
m_Retry :1,
m_Ignore :1,
m_Reset :1,
m_Close :1;
BITFIELD_END
wxString m_CustomLabel;
public:
MsgButtons() { bitset = 0; }
MsgButtons& OK() { m_OK = true; return *this; }
MsgButtons& Cancel() { m_Cancel = true; return *this; }
MsgButtons& Apply() { m_Apply = true; return *this; }
MsgButtons& Yes() { m_Yes = true; return *this; }
MsgButtons& No() { m_No = true; return *this; }
MsgButtons& ToAll() { m_AllowToAll = true; return *this; }
MsgButtons& Abort() { m_Abort = true; return *this; }
MsgButtons& Retry() { m_Retry = true; return *this; }
MsgButtons& Ignore() { m_Ignore = true; return *this; }
MsgButtons& Reset() { m_Reset = true; return *this; }
MsgButtons& Close() { m_Close = true; return *this; }
MsgButtons& Custom( const wxString& label)
{
m_CustomLabel = label;
return *this;
}
MsgButtons& OKCancel() { m_OK = m_Cancel = true; return *this; }
MsgButtons& YesNo() { m_Yes = m_No = true; return *this; }
bool HasOK() const { return m_OK; }
bool HasCancel() const { return m_Cancel; }
bool HasApply() const { return m_Apply; }
bool HasYes() const { return m_Yes; }
bool HasNo() const { return m_No; }
bool AllowsToAll() const{ return m_AllowToAll; }
bool HasAbort() const { return m_Abort; }
bool HasRetry() const { return m_Retry; }
bool HasIgnore() const { return m_Ignore; }
bool HasReset() const { return m_Reset; }
bool HasClose() const { return m_Close; }
bool HasCustom() const { return !m_CustomLabel.IsEmpty(); }
const wxString& GetCustomLabel() const { return m_CustomLabel; }
bool Allows( wxWindowID id ) const;
void SetBestFocus( wxWindow* dialog ) const;
void SetBestFocus( wxWindow& dialog ) const;
bool operator ==( const MsgButtons& right ) const
{
return OpEqu( bitset );
}
bool operator !=( const MsgButtons& right ) const
{
return !OpEqu( bitset );
}
};
// --------------------------------------------------------------------------------------
// ModalButtonPanel
@ -138,151 +41,7 @@ public:
virtual void OnActionButtonClicked( wxCommandEvent& evt );
};
// --------------------------------------------------------------------------------------
// BaseMessageBoxEvent
// --------------------------------------------------------------------------------------
class BaseMessageBoxEvent : public wxEvent
{
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(BaseMessageBoxEvent)
protected:
MsgboxEventResult* m_Instdata;
wxString m_Content;
public:
virtual ~BaseMessageBoxEvent() throw() { }
virtual BaseMessageBoxEvent *Clone() const { return new BaseMessageBoxEvent(*this); }
explicit BaseMessageBoxEvent( int msgtype=pxEvt_MessageBox, const wxString& content=wxEmptyString );
BaseMessageBoxEvent( MsgboxEventResult& instdata, const wxString& content );
BaseMessageBoxEvent( const wxString& content );
BaseMessageBoxEvent( const BaseMessageBoxEvent& event );
BaseMessageBoxEvent& SetInstData( MsgboxEventResult& instdata );
virtual void IssueDialog();
protected:
virtual int _DoDialog() const;
};
// --------------------------------------------------------------------------------------
// pxMessageBoxEvent
// --------------------------------------------------------------------------------------
// This event type is used to transfer message boxes to the main UI thread, and return the
// result of the box. It's the only way a message box can be issued from non-main threads
// with complete safety in wx2.8.
//
// For simplicity sake this message box only supports two basic designs. The main design
// is a generic message box with confirmation buttons of your choosing. Additionally you
// can specify a "scrollableContent" text string, which is added into a read-only richtext
// control similar to the console logs and such.
//
// Future consideration: If wxWidgets 3.0 has improved thread safety, then it should probably
// be reasonable for it to work with a more flexable model where the dialog can be created
// on a child thread, passed to the main thread, where ShowModal() is run (keeping the nested
// message pumps on the main thread where they belong). But so far this is not possible,
// because of various subtle issues in wx2.8 design.
//
class pxMessageBoxEvent : public BaseMessageBoxEvent
{
typedef BaseMessageBoxEvent _parent;
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxMessageBoxEvent)
protected:
wxString m_Title;
MsgButtons m_Buttons;
public:
virtual ~pxMessageBoxEvent() throw() { }
virtual pxMessageBoxEvent *Clone() const { return new pxMessageBoxEvent(*this); }
explicit pxMessageBoxEvent( int msgtype=pxEvt_MessageBox );
pxMessageBoxEvent( MsgboxEventResult& instdata, const wxString& title, const wxString& content, const MsgButtons& buttons );
pxMessageBoxEvent( const wxString& title, const wxString& content, const MsgButtons& buttons );
pxMessageBoxEvent( const pxMessageBoxEvent& event );
pxMessageBoxEvent& SetInstData( MsgboxEventResult& instdata );
protected:
virtual int _DoDialog() const;
};
// --------------------------------------------------------------------------------------
// pxAssertionEvent
// --------------------------------------------------------------------------------------
class pxAssertionEvent : public BaseMessageBoxEvent
{
typedef BaseMessageBoxEvent _parent;
DECLARE_DYNAMIC_CLASS_NO_ASSIGN( pxAssertionEvent )
protected:
wxString m_Stacktrace;
public:
virtual ~pxAssertionEvent() throw() { }
virtual pxAssertionEvent *Clone() const { return new pxAssertionEvent(*this); }
pxAssertionEvent();
pxAssertionEvent( MsgboxEventResult& instdata, const wxString& content, const wxString& trace );
pxAssertionEvent( const wxString& content, const wxString& trace );
pxAssertionEvent( const pxAssertionEvent& event );
pxAssertionEvent& SetInstData( MsgboxEventResult& instdata );
pxAssertionEvent& SetStacktrace( const wxString& trace );
protected:
virtual int _DoDialog() const;
};
// --------------------------------------------------------------------------------------
// 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
// --------------------------------------------------------------------------------------
class pxPingEvent : public wxEvent
{
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxPingEvent)
protected:
Semaphore* m_PostBack;
public:
virtual ~pxPingEvent() throw() { }
virtual pxPingEvent *Clone() const { return new pxPingEvent(*this); }
explicit pxPingEvent( int msgtype, Semaphore* sema=NULL );
explicit pxPingEvent( Semaphore* sema=NULL );
pxPingEvent( const pxPingEvent& src );
Semaphore* GetSemaphore() { return m_PostBack; }
};
typedef void FnType_VoidMethod();
typedef void FnType_Void();
// --------------------------------------------------------------------------------------
// wxAppWithHelpers
@ -290,15 +49,12 @@ typedef void FnType_VoidMethod();
class wxAppWithHelpers : public wxApp
{
typedef wxApp _parent;
DECLARE_DYNAMIC_CLASS(wxAppWithHelpers)
protected:
std::vector<Semaphore*> m_PingWhenIdle;
std::vector<IDeletableObject*> m_DeleteWhenIdle;
std::vector<wxEvent*> m_IdleEventQueue;
Threading::Mutex m_DeleteIdleLock;
wxTimer m_PingTimer;
Threading::Mutex m_IdleEventMutex;
wxTimer m_IdleEventTimer;
public:
@ -306,34 +62,59 @@ public:
virtual ~wxAppWithHelpers() {}
void CleanUp();
void DeleteObject( IDeletableObject& obj );
void DeleteObject( IDeletableObject* obj )
void DeleteObject( BaseDeletableObject& obj );
void DeleteObject( BaseDeletableObject* obj )
{
if( obj == NULL ) return;
DeleteObject( *obj );
}
void DeleteThread( Threading::PersistentThread& obj );
void DeleteThread( Threading::PersistentThread* obj )
{
if( obj == NULL ) return;
DeleteThread( *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 PostMethod( FnType_Void* method );
void PostIdleMethod( FnType_Void* method );
void ProcessMethod( void (*method)() );
sptr ProcessCommand( void* clientData, int evtType, int intParam=0, long longParam=0, const wxString& stringParam=wxEmptyString );
sptr ProcessCommand( int evtType, int intParam=0, long longParam=0, const wxString& stringParam=wxEmptyString );
void ProcessAction( pxInvokeActionEvent& evt );
void PostAction( const pxInvokeActionEvent& evt );
bool PostMethodMyself( void (*method)() );
void Ping();
bool OnInit();
//int OnExit();
void AddIdleEvent( const wxEvent& evt );
void PostEvent( const wxEvent& evt );
bool ProcessEvent( wxEvent& evt );
bool ProcessEvent( wxEvent* evt );
bool ProcessEvent( pxInvokeActionEvent& evt );
bool ProcessEvent( pxInvokeActionEvent* evt );
protected:
void IdleEventDispatcher( const char* action );
void PingDispatcher( const char* action );
void DeletionDispatcher();
void OnIdleEvent( wxIdleEvent& evt );
void OnPingEvent( pxPingEvent& evt );
void OnAddEventToIdleQueue( wxEvent& evt );
void OnPingTimeout( wxTimerEvent& evt );
void OnStartIdleEventTimer( wxEvent& evt );
void OnIdleEventTimeout( wxTimerEvent& evt );
void OnMessageBox( BaseMessageBoxEvent& evt );
void OnDeleteObject( wxCommandEvent& evt );
void OnDeleteThread( wxCommandEvent& evt );
void OnSynchronousCommand( pxSynchronousCommandEvent& evt );
void OnInvokeAction( pxInvokeActionEvent& evt );
};
namespace Msgbox

View File

@ -291,6 +291,10 @@ namespace pxSizerFlags
extern wxSizerFlags Checkbox();
};
BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE( pxEvt_OnThreadCleanup, -1 );
END_DECLARE_EVENT_TYPES()
// --------------------------------------------------------------------------------------
// wxDialogWithHelpers
// --------------------------------------------------------------------------------------

View File

@ -292,10 +292,11 @@ class FormatBuffer : public Mutex
public:
bool& clearbit;
SafeArray<CharType> buffer;
wxMBConvUTF8 ConvUTF8;
FormatBuffer( bool& bit_to_clear_on_destruction ) :
clearbit( bit_to_clear_on_destruction )
, buffer( 4096, wxsFormat( L"%s Format Buffer", (sizeof(CharType)==1) ? "Ascii" : "Unicode" ) )
FormatBuffer( bool& bit_to_clear_on_destruction )
: clearbit( bit_to_clear_on_destruction )
, buffer( 4096, wxsFormat( L"%s Format Buffer", (sizeof(CharType)==1) ? "Ascii" : "Unicode" ) )
{
}
@ -376,12 +377,20 @@ static wxString ascii_format_string(const char* fmt, va_list argptr)
{
if( ascii_buffer_is_deleted )
{
// This means that the program is shutting down and the C++ destructors are
// running, randomly deallocating static variables from existence. We handle it
// as gracefully as possible by allocating local vars to do our bidding (slow, but
// ultimately necessary!)
SafeArray<char> localbuf( 4096, L"Temporary Ascii Formatting Buffer" );
format_that_ascii_mess( localbuf, fmt, argptr );
return fromUTF8( localbuf.GetPtr() );
}
else
{
// This is normal operation. The static buffers are available for use, and we use
// them for sake of efficiency (fewer heap allocs, for sure!)
ScopedLock locker( ascii_buffer );
format_that_ascii_mess( ascii_buffer.buffer, fmt, argptr );
return fromUTF8( ascii_buffer.buffer.GetPtr() );
@ -391,6 +400,8 @@ static wxString ascii_format_string(const char* fmt, va_list argptr)
static wxString unicode_format_string(const wxChar* fmt, va_list argptr)
{
// See above for the explanation on the _is_deleted flags.
if( unicode_buffer_is_deleted )
{
SafeArray<wxChar> localbuf( 4096, L"Temporary Unicode Formatting Buffer" );

View File

@ -135,9 +135,9 @@ __forceinline void pxOnAssert( const DiagnosticOrigin& origin, const char* msg)
// Exception Namespace Implementations (Format message handlers for general exceptions)
// --------------------------------------------------------------------------------------
Exception::BaseException::~BaseException() throw() {}
BaseException::~BaseException() throw() {}
void Exception::BaseException::InitBaseEx( const wxString& msg_eng, const wxString& msg_xlt )
void BaseException::InitBaseEx( const wxString& msg_eng, const wxString& msg_xlt )
{
m_message_diag = msg_eng;
m_message_user = msg_xlt.IsEmpty() ? msg_eng : msg_xlt;
@ -155,7 +155,7 @@ void Exception::BaseException::InitBaseEx( const wxString& msg_eng, const wxStri
// given message is assumed to be a translation key, and will be stored in translated
// and untranslated forms.
void Exception::BaseException::InitBaseEx( const char* msg_eng )
void BaseException::InitBaseEx( const char* msg_eng )
{
m_message_diag = GetEnglish( msg_eng );
m_message_user = GetTranslation( msg_eng );
@ -166,11 +166,22 @@ void Exception::BaseException::InitBaseEx( const char* msg_eng )
#endif
}
wxString Exception::BaseException::FormatDiagnosticMessage() const
wxString BaseException::FormatDiagnosticMessage() const
{
return m_message_diag + L"\n\n" + m_stacktrace;
}
// ------------------------------------------------------------------------
Exception::RuntimeError::RuntimeError( const std::runtime_error& ex, const wxString& prefix )
{
const wxString msg( wxsFormat( L"%sSTL Runtime Error: %s",
(prefix.IsEmpty() ? prefix : wxsFormat(L"(%s) ", prefix)),
fromUTF8( ex.what() ).c_str()
) );
BaseException::InitBaseEx( msg, msg );
}
// ------------------------------------------------------------------------
wxString Exception::CancelEvent::FormatDiagnosticMessage() const
{

View File

@ -36,6 +36,8 @@ Threading::Mutex::Mutex()
pthread_mutex_init( &m_mutex, NULL );
}
static wxTimeSpan def_detach_timeout( 0, 0, 6, 0 );
void Threading::Mutex::Detach()
{
if( EBUSY != pthread_mutex_destroy(&m_mutex) ) return;
@ -52,7 +54,7 @@ void Threading::Mutex::Detach()
if( pxAssertDev( result != EBUSY, "Detachment of a recursively-locked mutex (self-locked!)." ) ) return;
}
if( Wait(def_deadlock_timeout) )
if( Wait(def_detach_timeout) )
pthread_mutex_destroy( &m_mutex );
else
Console.Error( "(Thread Log) Mutex cleanup failed due to possible deadlock.");
@ -65,7 +67,7 @@ Threading::Mutex::~Mutex() throw()
} DESTRUCTOR_CATCHALL;
}
Threading::MutexLockRecursive::MutexLockRecursive() : Mutex( false )
Threading::MutexRecursive::MutexRecursive() : Mutex( false )
{
if( _InterlockedIncrement( &_attr_refcount ) == 1 )
{
@ -79,7 +81,7 @@ Threading::MutexLockRecursive::MutexLockRecursive() : Mutex( false )
err = pthread_mutex_init( &m_mutex, &_attr_recursive );
}
Threading::MutexLockRecursive::~MutexLockRecursive() throw()
Threading::MutexRecursive::~MutexRecursive() throw()
{
if( _InterlockedDecrement( &_attr_refcount ) == 0 )
pthread_mutexattr_destroy( &_attr_recursive );
@ -100,7 +102,7 @@ void Threading::Mutex::Recreate()
// unlocked.
bool Threading::Mutex::RecreateIfLocked()
{
if( !Wait(def_deadlock_timeout) )
if( !Wait(def_detach_timeout) )
{
Recreate();
return true;
@ -151,8 +153,7 @@ void Threading::Mutex::Acquire()
}
else if( _WaitGui_RecursionGuard( "Mutex::Acquire" ) )
{
if( !AcquireWithoutYield(def_deadlock_timeout) )
throw Exception::ThreadDeadlock();
AcquireWithoutYield();
}
else
{
@ -177,12 +178,6 @@ bool Threading::Mutex::Acquire( const wxTimeSpan& timeout )
else if( _WaitGui_RecursionGuard( "Mutex::Acquire(timeout)" ) )
{
ScopedBusyCursor hourglass( Cursor_ReallyBusy );
if( timeout > def_deadlock_timeout )
{
if( AcquireWithoutYield(def_deadlock_timeout) ) return true;
throw Exception::ThreadDeadlock();
}
return AcquireWithoutYield( timeout );
}
else
@ -199,9 +194,6 @@ bool Threading::Mutex::Acquire( const wxTimeSpan& timeout )
return countdown.GetMilliseconds() > 0;
}
// Looks like a potential deadlock; throw an exception!
throw Exception::ThreadDeadlock();
#else
return AcquireWithoutYield();
#endif
@ -223,6 +215,12 @@ void Threading::Mutex::Wait()
Release();
}
void Threading::Mutex::WaitWithoutYield()
{
AcquireWithoutYield();
Release();
}
// Performs a wait on a locked mutex, or returns instantly if the mutex is unlocked.
// (Implemented internally as a simple Acquire/Release pair.)
//
@ -243,3 +241,69 @@ bool Threading::Mutex::Wait( const wxTimeSpan& timeout )
return false;
}
bool Threading::Mutex::WaitWithoutYield( const wxTimeSpan& timeout )
{
if( AcquireWithoutYield(timeout) )
{
Release();
return true;
}
return false;
}
// --------------------------------------------------------------------------------------
// ScopedLock Implementations
// --------------------------------------------------------------------------------------
Threading::ScopedLock::~ScopedLock() throw()
{
if( m_IsLocked && m_lock )
m_lock->Release();
}
Threading::ScopedLock::ScopedLock( const Mutex* locker )
{
AssignAndLock( locker );
}
Threading::ScopedLock::ScopedLock( const Mutex& locker )
{
AssignAndLock( locker );
}
void Threading::ScopedLock::AssignAndLock( const Mutex& locker )
{
AssignAndLock( &locker );
}
void Threading::ScopedLock::AssignAndLock( const Mutex* locker )
{
m_lock = const_cast<Mutex*>(locker);
if( !m_lock ) return;
m_IsLocked = true;
m_lock->Acquire();
}
// Provides manual unlocking of a scoped lock prior to object destruction.
void Threading::ScopedLock::Release()
{
if( !m_IsLocked ) return;
m_IsLocked = false;
if( m_lock ) m_lock->Release();
}
// provides manual locking of a scoped lock, to re-lock after a manual unlocking.
void Threading::ScopedLock::Acquire()
{
if( m_IsLocked || !m_lock ) return;
m_lock->Acquire();
m_IsLocked = true;
}
Threading::ScopedLock::ScopedLock( const Mutex& locker, bool isTryLock )
{
m_lock = const_cast<Mutex*>(&locker);
if( !m_lock ) return;
m_IsLocked = isTryLock ? m_lock->TryAcquire() : false;
}

View File

@ -0,0 +1,112 @@
/* 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 "RwMutex.h"
// --------------------------------------------------------------------------------------
// RwMutex
// --------------------------------------------------------------------------------------
Threading::RwMutex::RwMutex()
{
pthread_rwlock_init( &m_rwlock, NULL );
}
Threading::RwMutex::~RwMutex() throw()
{
pthread_rwlock_destroy( &m_rwlock );
}
void Threading::RwMutex::AcquireRead()
{
pthread_rwlock_rdlock( &m_rwlock );
}
void Threading::RwMutex::AcquireWrite()
{
pthread_rwlock_wrlock( &m_rwlock );
}
bool Threading::RwMutex::TryAcquireRead()
{
return pthread_rwlock_tryrdlock( &m_rwlock ) != EBUSY;
}
bool Threading::RwMutex::TryAcquireWrite()
{
return pthread_rwlock_trywrlock( &m_rwlock ) != EBUSY;
}
void Threading::RwMutex::Release()
{
pthread_rwlock_unlock( &m_rwlock );
}
// --------------------------------------------------------------------------------------
//
// --------------------------------------------------------------------------------------
Threading::BaseScopedReadWriteLock::~BaseScopedReadWriteLock() throw()
{
if( m_IsLocked )
m_lock.Release();
}
// Provides manual unlocking of a scoped lock prior to object destruction.
void Threading::BaseScopedReadWriteLock::Release()
{
if( !m_IsLocked ) return;
m_IsLocked = false;
m_lock.Release();
}
// --------------------------------------------------------------------------------------
// ScopedReadLock / ScopedWriteLock
// --------------------------------------------------------------------------------------
Threading::ScopedReadLock::ScopedReadLock( RwMutex& locker )
: BaseScopedReadWriteLock( locker )
{
m_IsLocked = true;
m_lock.AcquireRead();
}
// provides manual locking of a scoped lock, to re-lock after a manual unlocking.
void Threading::ScopedReadLock::Acquire()
{
if( m_IsLocked ) return;
m_lock.AcquireRead();
m_IsLocked = true;
}
Threading::ScopedWriteLock::ScopedWriteLock( RwMutex& locker )
: BaseScopedReadWriteLock( locker )
{
m_IsLocked = true;
m_lock.AcquireWrite();
}
// provides manual locking of a scoped lock, to re-lock after a manual unlocking.
void Threading::ScopedWriteLock::Acquire()
{
if( m_IsLocked ) return;
m_lock.AcquireWrite();
m_IsLocked = true;
}
// Special constructor used by ScopedTryLock
Threading::ScopedWriteLock::ScopedWriteLock( RwMutex& locker, bool isTryLock )
: BaseScopedReadWriteLock( locker )
{
//m_IsLocked = isTryLock ? m_lock.TryAcquireWrite() : false;
}

View File

@ -92,8 +92,7 @@ void Threading::Semaphore::Wait()
else if( _WaitGui_RecursionGuard( "Semaphore::Wait" ) )
{
ScopedBusyCursor hourglass( Cursor_ReallyBusy );
if( !WaitWithoutYield(def_yieldgui_interval) ) // default is 4 seconds
throw Exception::ThreadDeadlock();
WaitWithoutYield();
}
else
{
@ -128,11 +127,6 @@ bool Threading::Semaphore::Wait( const wxTimeSpan& timeout )
else if( _WaitGui_RecursionGuard( "Semaphore::Wait(timeout)" ) )
{
ScopedBusyCursor hourglass( Cursor_ReallyBusy );
if( timeout > def_deadlock_timeout )
{
if( WaitWithoutYield(def_deadlock_timeout) ) return true;
throw Exception::ThreadDeadlock();
}
return WaitWithoutYield( timeout );
}
else
@ -165,7 +159,8 @@ void Threading::Semaphore::WaitNoCancel()
{
int oldstate;
pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate );
WaitWithoutYield();
//WaitWithoutYield();
Wait();
pthread_setcancelstate( oldstate, NULL );
}
@ -173,7 +168,8 @@ void Threading::Semaphore::WaitNoCancel( const wxTimeSpan& timeout )
{
int oldstate;
pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate );
WaitWithoutYield( timeout );
//WaitWithoutYield( timeout );
Wait( timeout );
pthread_setcancelstate( oldstate, NULL );
}

View File

@ -20,7 +20,16 @@ const wxRect wxDefaultRect( wxDefaultCoord, wxDefaultCoord, wxDefaultCoord, wxDe
__forceinline wxString fromUTF8( const char* src )
{
return wxString::FromUTF8( src );
// IMPORTANT: We cannot use wxString::FromUTF8 because it *stupidly* relies on a C++ global instance of
// wxMBConvUTF8(). C++ initializes and destroys these globals at random, so any object constructor or
// destructor that attempts to do logging may crash the app (either during startup or during exit) unless
// we use a LOCAL instance of wxMBConvUTF8(). --air
// Performance? No worries. wxMBConvUTF8() is virtually free. Initializing a stack copy of the class
// is just as efficient as passing a pointer to a pre-instanced global. (which makes me wonder wh wxWidgets
// uses the stupid globals in the first place!) --air
return wxString( src, wxMBConvUTF8() );
}
__forceinline wxString fromAscii( const char* src )

View File

@ -33,15 +33,33 @@ template class EventSource< EventListener_Thread >;
// to avoid gui deadlock).
const wxTimeSpan Threading::def_yieldgui_interval( 0, 0, 0, 100 );
// three second interval for deadlock protection on waitgui.
const wxTimeSpan Threading::def_deadlock_timeout( 0, 0, 3, 0 );
class StaticMutex : public Mutex
{
protected:
bool& m_DeletedFlag;
public:
StaticMutex( bool& deletedFlag )
: m_DeletedFlag( deletedFlag )
{
}
virtual ~StaticMutex() throw()
{
m_DeletedFlag = true;
}
};
static pthread_key_t curthread_key = NULL;
static s32 total_key_count = 0;
static Mutex total_key_lock;
static bool tkl_destructed = false;
static StaticMutex total_key_lock( tkl_destructed );
static void make_curthread_key()
{
pxAssumeDev( !tkl_destructed, "total_key_lock is destroyed; program is shutting down; cannot create new thread key." );
ScopedLock lock( total_key_lock );
if( total_key_count++ != 0 ) return;
@ -54,7 +72,10 @@ static void make_curthread_key()
static void unmake_curthread_key()
{
ScopedLock lock( total_key_lock );
ScopedLock lock;
if( !tkl_destructed )
lock.AssignAndLock( total_key_lock );
if( --total_key_count > 0 ) return;
if( curthread_key != NULL )
@ -63,6 +84,11 @@ static void unmake_curthread_key()
curthread_key = NULL;
}
void Threading::pxTestCancel()
{
pthread_testcancel();
}
// Returns a handle to the current persistent thread. If the current thread does not belong
// to the PersistentThread table, NULL is returned. Since the main/ui thread is not created
// through PersistentThread it will also return NULL. Callers can use wxThread::IsMain() to
@ -158,20 +184,6 @@ Threading::PersistentThread::~PersistentThread() throw()
Threading::Sleep( 1 );
Detach();
}
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
// and semaphores all the time. And terminating threads isn't really cross-platform
// either so let's just not bother.
// Additionally since this is a destructor most of our derived class info is lost,
// so we can't allow for customized deadlock handlers, least not in any useful
// context. So let's just log the condition and move on.
Console.Error( L"(Thread Log) Thread destructor for '%s' timed out with error:\n\t",
m_name.c_str(), ex.FormatDiagnosticMessage().c_str() );
}
DESTRUCTOR_CATCHALL
}
@ -265,7 +277,6 @@ bool Threading::PersistentThread::Detach()
bool Threading::PersistentThread::_basecancel()
{
// Prevent simultaneous startup and cancel:
if( !m_running ) return false;
if( m_detached )
@ -339,6 +350,12 @@ void Threading::PersistentThread::Block()
WaitOnSelf( m_lock_InThread );
}
bool Threading::PersistentThread::Block( const wxTimeSpan& timeout )
{
AffinityAssert_DisallowFromSelf(pxDiagSpot);
return WaitOnSelf( m_lock_InThread, timeout );
}
bool Threading::PersistentThread::IsSelf() const
{
// Detached threads may have their pthread handles recycled as newer threads, causing
@ -402,7 +419,7 @@ void Threading::PersistentThread::WaitOnSelf( Semaphore& sem ) const
while( true )
{
if( sem.Wait( wxTimeSpan(0, 0, 0, 333) ) ) return;
if( sem.WaitWithoutYield( wxTimeSpan(0, 0, 0, 333) ) ) return;
_selfRunningTest( L"semaphore" );
}
}
@ -426,7 +443,7 @@ void Threading::PersistentThread::WaitOnSelf( Mutex& mutex ) const
while( true )
{
if( mutex.Wait( wxTimeSpan(0, 0, 0, 333) ) ) return;
if( mutex.WaitWithoutYield( wxTimeSpan(0, 0, 0, 333) ) ) return;
_selfRunningTest( L"mutex" );
}
}
@ -442,7 +459,7 @@ bool Threading::PersistentThread::WaitOnSelf( Semaphore& sem, const wxTimeSpan&
while( runningout.GetMilliseconds() > 0 )
{
const wxTimeSpan interval( (SelfWaitInterval < runningout) ? SelfWaitInterval : runningout );
if( sem.Wait( interval ) ) return true;
if( sem.WaitWithoutYield( interval ) ) return true;
_selfRunningTest( L"semaphore" );
runningout -= interval;
}
@ -458,7 +475,7 @@ bool Threading::PersistentThread::WaitOnSelf( Mutex& mutex, const wxTimeSpan& ti
while( runningout.GetMilliseconds() > 0 )
{
const wxTimeSpan interval( (SelfWaitInterval < runningout) ? SelfWaitInterval : runningout );
if( mutex.Wait( interval ) ) return true;
if( mutex.WaitWithoutYield( interval ) ) return true;
_selfRunningTest( L"mutex" );
runningout -= interval;
}
@ -487,17 +504,7 @@ void Threading::PersistentThread::_try_virtual_invoke( void (PersistentThread::*
//
catch( std::runtime_error& ex )
{
m_except = new Exception::RuntimeError(
// Diagnostic message:
wxsFormat( L"(thread: %s) STL Runtime Error: %s",
GetName().c_str(), fromUTF8( ex.what() ).c_str()
),
// User Message (not translated, std::exception doesn't have that kind of fancy!
wxsFormat( L"A runtime error occurred in %s:\n\n%s (STL)",
GetName().c_str(), fromUTF8( ex.what() ).c_str()
)
);
m_except = new Exception::RuntimeError( ex, GetName().c_str() );
}
// ----------------------------------------------------------------------------
@ -513,20 +520,20 @@ void Threading::PersistentThread::_try_virtual_invoke( void (PersistentThread::*
// the MSVC debugger (or by silent random annoying fail on debug-less linux).
/*catch( std::logic_error& ex )
{
throw Exception::BaseException( wxsFormat( L"(thread: %s) STL Logic Error: %s",
throw BaseException( wxsFormat( L"(thread: %s) STL Logic Error: %s",
GetName().c_str(), fromUTF8( ex.what() ).c_str() )
);
}
catch( std::exception& ex )
{
throw Exception::BaseException( wxsFormat( L"(thread: %s) STL exception: %s",
throw BaseException( wxsFormat( L"(thread: %s) STL exception: %s",
GetName().c_str(), fromUTF8( ex.what() ).c_str() )
);
}*/
// ----------------------------------------------------------------------------
// BaseException -- same deal as LogicErrors.
//
catch( Exception::BaseException& ex )
catch( BaseException& ex )
{
m_except = ex.Clone();
m_except->DiagMsg() = wxsFormat( L"(thread:%s) ", GetName().c_str() ) + m_except->DiagMsg();
@ -541,6 +548,10 @@ void Threading::PersistentThread::_ThreadCleanup()
AffinityAssert_AllowFromSelf(pxDiagSpot);
_try_virtual_invoke( &PersistentThread::OnCleanupInThread );
m_lock_InThread.Release();
// Must set m_running LAST, as thread destructors depend on this value (it is used
// to avoid destruction of the thread until all internal data use has stopped.
m_running = false;
}
wxString Threading::PersistentThread::GetName() const
@ -590,12 +601,10 @@ void Threading::PersistentThread::OnStart()
m_sem_startup.Reset();
}
// Extending classes that override this method shoul always call it last from their
// Extending classes that override this method should always call it last from their
// personal implementations.
void Threading::PersistentThread::OnCleanupInThread()
{
m_running = false;
if( curthread_key != NULL )
pthread_setspecific( curthread_key, NULL );
@ -605,6 +614,8 @@ void Threading::PersistentThread::OnCleanupInThread()
m_native_handle = NULL;
m_native_id = 0;
m_evtsrc_OnDelete.Dispatch( 0 );
}
// passed into pthread_create, and is used to dispatch the thread's object oriented

View File

@ -22,7 +22,6 @@
namespace Threading
{
extern const wxTimeSpan def_yieldgui_interval;
extern const wxTimeSpan def_deadlock_timeout;
extern bool _WaitGui_RecursionGuard( const char* guardname );
}

View File

@ -16,14 +16,19 @@
#include "PrecompiledHeader.h"
#include "wxAppWithHelpers.h"
DEFINE_EVENT_TYPE( pxEvt_Ping );
DEFINE_EVENT_TYPE( pxEvt_IdleEventQueue );
DEFINE_EVENT_TYPE( pxEvt_MessageBox );
#include "PersistentThread.h"
DEFINE_EVENT_TYPE( pxEvt_DeleteObject );
//DEFINE_EVENT_TYPE( pxEvt_Assertion );
DEFINE_EVENT_TYPE( pxEvt_DeleteThread );
DEFINE_EVENT_TYPE( pxEvt_StartIdleEventTimer );
DEFINE_EVENT_TYPE( pxEvt_InvokeAction );
DEFINE_EVENT_TYPE( pxEvt_SynchronousCommand );
void IDeletableObject::DoDeletion()
IMPLEMENT_DYNAMIC_CLASS( pxSimpleEvent, wxEvent )
void BaseDeletableObject::DoDeletion()
{
wxAppWithHelpers* app = wxDynamicCast( wxApp::GetInstance(), wxAppWithHelpers );
pxAssume( app != NULL );
@ -31,27 +36,171 @@ void IDeletableObject::DoDeletion()
}
// --------------------------------------------------------------------------------------
// pxPingEvent Implementations
// pxInvokeActionEvent Implementations
// --------------------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS( pxPingEvent, wxEvent )
IMPLEMENT_DYNAMIC_CLASS( pxInvokeActionEvent, wxEvent )
pxPingEvent::pxPingEvent( int msgtype, Semaphore* sema )
pxInvokeActionEvent::pxInvokeActionEvent( SynchronousActionState* sema, int msgtype )
: wxEvent( 0, msgtype )
{
m_PostBack = sema;
m_state = sema;
}
pxPingEvent::pxPingEvent( Semaphore* sema )
: wxEvent( 0, pxEvt_Ping )
pxInvokeActionEvent::pxInvokeActionEvent( SynchronousActionState& sema, int msgtype )
: wxEvent( 0, msgtype )
{
m_PostBack = sema;
m_state = &sema;
}
pxPingEvent::pxPingEvent( const pxPingEvent& src )
pxInvokeActionEvent::pxInvokeActionEvent( const pxInvokeActionEvent& src )
: wxEvent( src )
{
m_PostBack = src.m_PostBack;
m_state = src.m_state;
}
void pxInvokeActionEvent::SetException( const BaseException& ex )
{
SetException( ex.Clone() );
}
void pxInvokeActionEvent::SetException( BaseException* ex )
{
const wxString& prefix( wxsFormat(L"(%s) ", GetClassInfo()->GetClassName()) );
ex->DiagMsg() = prefix + ex->DiagMsg();
if( !m_state )
{
ScopedPtr<BaseException> exptr( ex ); // auto-delete it after handling.
ex->Rethrow();
}
m_state->SetException( ex );
}
// --------------------------------------------------------------------------------------
// pxSynchronousCommandEvent
// --------------------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS( pxSynchronousCommandEvent, wxCommandEvent )
pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState* sema, wxEventType commandType, int winid)
: wxCommandEvent( pxEvt_SynchronousCommand, winid )
{
m_sync = sema;
m_realEvent = commandType;
}
pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState& sema, wxEventType commandType, int winid)
: wxCommandEvent( pxEvt_SynchronousCommand )
{
m_sync = &sema;
m_realEvent = commandType;
}
pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState* sema, const wxCommandEvent& evt )
: wxCommandEvent( evt )
{
m_sync = sema;
m_realEvent = evt.GetEventType();
SetEventType( pxEvt_SynchronousCommand );
}
pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState& sema, const wxCommandEvent& evt )
: wxCommandEvent( evt )
{
m_sync = &sema;
m_realEvent = evt.GetEventType();
SetEventType( pxEvt_SynchronousCommand );
}
pxSynchronousCommandEvent::pxSynchronousCommandEvent( const pxSynchronousCommandEvent& src )
: wxCommandEvent( src )
{
m_sync = src.m_sync;
m_realEvent = src.m_realEvent;
}
void pxSynchronousCommandEvent::SetException( const BaseException& ex )
{
if( !m_sync ) ex.Rethrow();
m_sync->SetException( ex );
}
void pxSynchronousCommandEvent::SetException( BaseException* ex )
{
if( !m_sync )
{
ScopedPtr<BaseException> exptr( ex ); // auto-delete it after handling.
ex->Rethrow();
}
m_sync->SetException( ex );
}
// --------------------------------------------------------------------------------------
// pxInvokeMethodEvent
// --------------------------------------------------------------------------------------
// Unlike pxPingEvent, the Semaphore belonging to this event is typically posted when the
// invoked method is completed. If the method can be executed in non-blocking fashion then
// it should leave the semaphore postback NULL.
//
class pxInvokeMethodEvent : public pxInvokeActionEvent
{
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxInvokeMethodEvent)
typedef pxInvokeActionEvent _parent;
protected:
void (*m_Method)();
public:
virtual ~pxInvokeMethodEvent() throw() { }
virtual pxInvokeMethodEvent *Clone() const { return new pxInvokeMethodEvent(*this); }
explicit pxInvokeMethodEvent( void (*method)()=NULL, SynchronousActionState* sema=NULL )
: pxInvokeActionEvent( sema )
{
m_Method = method;
}
explicit pxInvokeMethodEvent( void (*method)(), SynchronousActionState& sema )
: pxInvokeActionEvent( sema )
{
m_Method = method;
}
pxInvokeMethodEvent( const pxInvokeMethodEvent& src )
: pxInvokeActionEvent( src )
{
m_Method = src.m_Method;
}
void SetMethod( void (*method)() )
{
m_Method = method;
}
protected:
void _DoInvoke()
{
if( m_Method ) m_Method();
}
};
IMPLEMENT_DYNAMIC_CLASS( pxInvokeMethodEvent, pxInvokeActionEvent )
// --------------------------------------------------------------------------------------
// pxExceptionEvent implementations
// --------------------------------------------------------------------------------------
pxExceptionEvent::pxExceptionEvent( const BaseException& ex )
{
m_except = ex.Clone();
}
void pxExceptionEvent::_DoInvoke()
{
ScopedPtr<BaseException> deleteMe( m_except );
if( deleteMe ) deleteMe->Rethrow();
}
// --------------------------------------------------------------------------------------
@ -64,81 +213,192 @@ pxPingEvent::pxPingEvent( const pxPingEvent& src )
//
IMPLEMENT_DYNAMIC_CLASS( wxAppWithHelpers, wxApp )
// Posts a method to the main thread; non-blocking. Post occurs even when called from the
// main thread.
void wxAppWithHelpers::PostMethod( FnType_Void* method )
{
PostEvent( pxInvokeMethodEvent( method ) );
}
// Posts a method to the main thread; non-blocking. Post occurs even when called from the
// main thread.
void wxAppWithHelpers::PostIdleMethod( FnType_Void* method )
{
pxInvokeMethodEvent evt( method );
AddIdleEvent( evt );
}
bool wxAppWithHelpers::PostMethodMyself( void (*method)() )
{
if( wxThread::IsMain() ) return false;
PostEvent( pxInvokeMethodEvent( method ) );
return true;
}
void wxAppWithHelpers::ProcessMethod( void (*method)() )
{
if( wxThread::IsMain() )
{
method();
return;
}
SynchronousActionState sync;
PostEvent( pxInvokeMethodEvent( method, sync ) );
sync.WaitForResult();
}
void wxAppWithHelpers::PostEvent( const wxEvent& evt )
{
// Const Cast is OK!
// Truth is, AddPendingEvent should be a const-qualified parameter, as
// it makes an immediate clone copy of the event -- but wxWidgets
// fails again in structured C/C++ design design. So I'm forcing it as such
// here. -- air
_parent::AddPendingEvent( const_cast<wxEvent&>(evt) );
}
bool wxAppWithHelpers::ProcessEvent( wxEvent& evt )
{
// Note: We can't do an automatic blocking post of the message here, because wxWidgets
// isn't really designed for it (some events return data to the caller via the event
// struct, and posting the event would require a temporary clone, where changes would
// be lost).
AffinityAssert_AllowFrom_MainUI();
return _parent::ProcessEvent( evt );
}
bool wxAppWithHelpers::ProcessEvent( wxEvent* evt )
{
AffinityAssert_AllowFrom_MainUI();
ScopedPtr<wxEvent> deleteMe( evt );
return _parent::ProcessEvent( *deleteMe );
}
bool wxAppWithHelpers::ProcessEvent( pxInvokeActionEvent& evt )
{
if( wxThread::IsMain() )
return _parent::ProcessEvent( evt );
else
{
SynchronousActionState sync;
evt.SetSyncState( sync );
AddPendingEvent( evt );
sync.WaitForResult();
return true;
}
}
bool wxAppWithHelpers::ProcessEvent( pxInvokeActionEvent* evt )
{
if( wxThread::IsMain() )
{
ScopedPtr<wxEvent> deleteMe( evt );
return _parent::ProcessEvent( *deleteMe );
}
else
{
SynchronousActionState sync;
evt->SetSyncState( sync );
AddPendingEvent( *evt );
sync.WaitForResult();
return true;
}
}
void wxAppWithHelpers::CleanUp()
{
DeletionDispatcher();
// I'm pretty sure the message pump is dead by now, which means we need to run through
// idle event list by hand and process the pending Deletion messages (all others can be
// ignored -- it's only deletions we want handled, and really we *could* ignore those too
// but I like to be tidy. -- air
//IdleEventDispatcher( "CleanUp" );
//DeletionDispatcher();
_parent::CleanUp();
}
void wxAppWithHelpers::OnPingEvent( pxPingEvent& evt )
void pxInvokeActionEvent::InvokeAction()
{
// Ping events are dispatched during the idle event handler, which ensures
// the ping is posted only after all other pending messages behind the ping
// are also processed.
AffinityAssert_AllowFrom_MainUI();
if( m_PingWhenIdle.size() == 0 ) m_PingTimer.Start( 200, true );
m_PingWhenIdle.push_back( evt.GetSemaphore() );
try {
_DoInvoke();
}
catch( BaseException& ex )
{
SetException( ex );
}
catch( std::runtime_error& ex )
{
SetException( new Exception::RuntimeError( ex ) );
}
if( m_state ) m_state->PostResult();
}
void wxAppWithHelpers::OnAddEventToIdleQueue( wxEvent& evt )
void wxAppWithHelpers::OnSynchronousCommand( pxSynchronousCommandEvent& evt )
{
if( m_IdleEventQueue.size() == 0 ) m_IdleEventTimer.Start( 100, true );
AffinityAssert_AllowFrom_MainUI();
evt.SetEventType( evt.GetRealEventType() );
try {
ProcessEvent( evt );
}
catch( BaseException& ex )
{
evt.SetException( ex );
}
catch( std::runtime_error& ex )
{
evt.SetException( new Exception::RuntimeError( ex, evt.GetClassInfo()->GetClassName() ) );
}
if( Semaphore* sema = evt.GetSemaphore() ) sema->Post();
}
void wxAppWithHelpers::AddIdleEvent( const wxEvent& evt )
{
ScopedLock lock( m_IdleEventMutex );
if( m_IdleEventQueue.size() == 0 )
PostEvent( pxSimpleEvent( pxEvt_StartIdleEventTimer ) );
m_IdleEventQueue.push_back( evt.Clone() );
}
void wxAppWithHelpers::OnStartIdleEventTimer( wxEvent& evt )
{
ScopedLock lock( m_IdleEventMutex );
if( m_IdleEventQueue.size() != 0 )
m_IdleEventTimer.Start( 100, true );
}
void wxAppWithHelpers::IdleEventDispatcher( const char* action )
{
ScopedLock lock( m_IdleEventMutex );
size_t size = m_IdleEventQueue.size();
if( size == 0 ) return;
DbgCon.WriteLn( Color_Gray, "App IdleQueue (%s) -> %u events.", action, size );
for( size_t i=0; i<size; ++i )
{
ProcessEvent( *m_IdleEventQueue[i] );
}
m_IdleEventQueue.clear();
}
void wxAppWithHelpers::PingDispatcher( const char* action )
{
size_t size = m_PingWhenIdle.size();
if( size == 0 ) return;
DbgCon.WriteLn( Color_Gray, "App Event Ping (%s) -> %u listeners.", action, size );
for( size_t i=0; i<size; ++i )
{
if( Semaphore* sema = m_PingWhenIdle[i] ) sema->Post();
}
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 )
{
m_PingTimer.Stop();
m_IdleEventTimer.Stop();
PingDispatcher( "Idle" );
IdleEventDispatcher( "Idle" );
}
void wxAppWithHelpers::OnPingTimeout( wxTimerEvent& evt )
{
PingDispatcher( "Timeout" );
}
void wxAppWithHelpers::OnIdleEventTimeout( wxTimerEvent& evt )
{
IdleEventDispatcher( "Timeout" );
@ -148,10 +408,10 @@ void wxAppWithHelpers::Ping()
{
DbgCon.WriteLn( Color_Gray, L"App Event Ping Requested from %s thread.", pxGetCurrentThreadName().c_str() );
Semaphore sema;
pxPingEvent evt( &sema );
AddPendingEvent( evt );
sema.WaitNoCancel();
SynchronousActionState sync;
pxInvokeActionEvent evt( sync );
AddIdleEvent( evt );
sync.WaitForResult();
}
void wxAppWithHelpers::PostCommand( void* clientData, int evtType, int intParam, long longParam, const wxString& stringParam )
@ -169,52 +429,106 @@ void wxAppWithHelpers::PostCommand( int evtType, int intParam, long longParam, c
PostCommand( NULL, evtType, intParam, longParam, stringParam );
}
void wxAppWithHelpers::DeleteObject( IDeletableObject& obj )
sptr wxAppWithHelpers::ProcessCommand( void* clientData, int evtType, int intParam, long longParam, const wxString& stringParam )
{
pxAssume( obj.IsBeingDeleted() );
ScopedLock lock( m_DeleteIdleLock );
m_DeleteWhenIdle.push_back( &obj );
SynchronousActionState sync;
pxSynchronousCommandEvent evt( sync, evtType );
evt.SetClientData( clientData );
evt.SetInt( intParam );
evt.SetExtraLong( longParam );
evt.SetString( stringParam );
AddPendingEvent( evt );
sync.WaitForResult();
return sync.return_value;
}
typedef void (wxEvtHandler::*BaseMessageBoxEventFunction)(BaseMessageBoxEvent&);
typedef void (wxEvtHandler::*pxPingEventFunction)(pxPingEvent&);
sptr wxAppWithHelpers::ProcessCommand( int evtType, int intParam, long longParam, const wxString& stringParam )
{
return ProcessCommand( NULL, evtType, intParam, longParam, stringParam );
}
void wxAppWithHelpers::PostAction( const pxInvokeActionEvent& evt )
{
PostEvent( evt );
}
void wxAppWithHelpers::ProcessAction( pxInvokeActionEvent& evt )
{
if( !wxThread::IsMain() )
{
SynchronousActionState sync;
evt.SetSyncState( sync );
AddPendingEvent( evt );
sync.WaitForResult();
}
else
evt.InvokeAction();
}
void wxAppWithHelpers::DeleteObject( BaseDeletableObject& obj )
{
pxAssume( !obj.IsBeingDeleted() );
wxCommandEvent evt( pxEvt_DeleteObject );
evt.SetClientData( (void*)&obj );
AddIdleEvent( evt );
}
void wxAppWithHelpers::DeleteThread( PersistentThread& obj )
{
//pxAssume( obj.IsBeingDeleted() );
wxCommandEvent evt( pxEvt_DeleteThread );
evt.SetClientData( (void*)&obj );
AddIdleEvent( evt );
}
typedef void (wxEvtHandler::*pxInvokeActionEventFunction)(pxInvokeActionEvent&);
bool wxAppWithHelpers::OnInit()
{
#define pxMessageBoxEventThing(func) \
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(BaseMessageBoxEventFunction, &func )
#define pxActionEventHandler(func) \
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(pxInvokeActionEventFunction, &func )
#define pxPingEventHandler(func) \
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(pxPingEventFunction, &func )
Connect( pxEvt_SynchronousCommand, pxSynchronousEventHandler (wxAppWithHelpers::OnSynchronousCommand) );
Connect( pxEvt_InvokeAction, pxActionEventHandler (wxAppWithHelpers::OnInvokeAction) );
Connect( pxEvt_StartIdleEventTimer, wxEventHandler (wxAppWithHelpers::OnStartIdleEventTimer) );
Connect( pxEvt_MessageBox, pxMessageBoxEventThing (wxAppWithHelpers::OnMessageBox) );
//Connect( pxEvt_Assertion, pxMessageBoxEventThing (wxAppWithHelpers::OnMessageBox) );
Connect( pxEvt_Ping, pxPingEventHandler (wxAppWithHelpers::OnPingEvent) );
Connect( pxEvt_IdleEventQueue, wxEventHandler (wxAppWithHelpers::OnAddEventToIdleQueue) );
Connect( pxEvt_DeleteObject, wxCommandEventHandler (wxAppWithHelpers::OnDeleteObject) );
Connect( wxEVT_IDLE, wxIdleEventHandler (wxAppWithHelpers::OnIdleEvent) );
Connect( pxEvt_DeleteObject, wxCommandEventHandler (wxAppWithHelpers::OnDeleteObject) );
Connect( pxEvt_DeleteThread, wxCommandEventHandler (wxAppWithHelpers::OnDeleteThread) );
Connect( wxEVT_IDLE, wxIdleEventHandler (wxAppWithHelpers::OnIdleEvent) );
Connect( m_PingTimer.GetId(), wxEVT_TIMER, wxTimerEventHandler(wxAppWithHelpers::OnPingTimeout) );
Connect( m_IdleEventTimer.GetId(), wxEVT_TIMER, wxTimerEventHandler(wxAppWithHelpers::OnIdleEventTimeout) );
return _parent::OnInit();
}
void wxAppWithHelpers::OnMessageBox( BaseMessageBoxEvent& evt )
void wxAppWithHelpers::OnInvokeAction( pxInvokeActionEvent& evt )
{
evt.IssueDialog();
evt.InvokeAction(); // wow this is easy!
}
void wxAppWithHelpers::OnDeleteObject( wxCommandEvent& evt )
{
if( evt.GetClientData() == NULL ) return;
delete (IDeletableObject*)evt.GetClientData();
delete (BaseDeletableObject*)evt.GetClientData();
}
// Threads have their own deletion handler that propagates exceptions thrown by the thread to the UI.
// (thus we have a fairly automatic threaded exception system!)
void wxAppWithHelpers::OnDeleteThread( wxCommandEvent& evt )
{
ScopedPtr<PersistentThread> thr( (PersistentThread*)evt.GetClientData() );
if( !thr ) return;
thr->RethrowException();
}
wxAppWithHelpers::wxAppWithHelpers()
: m_PingTimer( this )
, m_IdleEventTimer( this )
: m_IdleEventTimer( this )
{
#ifdef __WXMSW__
// This variable assignment ensures that MSVC links in the TLS setup stubs even in

View File

@ -25,37 +25,39 @@
using namespace pxSizerFlags;
DEFINE_EVENT_TYPE( pxEvt_OnThreadCleanup );
// --------------------------------------------------------------------------------------
// IDeletableObject Implementation
// BaseDeletableObject 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()
bool BaseDeletableObject::MarkForDeletion()
{
return !_InterlockedExchange( &m_IsBeingDeleted, true );
}
void IDeletableObject::DeleteSelf()
void BaseDeletableObject::DeleteSelf()
{
if( MarkForDeletion() )
DoDeletion();
}
IDeletableObject::IDeletableObject()
BaseDeletableObject::BaseDeletableObject()
{
#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!" );
//pxAssertDev( _CrtIsValidHeapPointer( this ), "BaseDeletableObject types cannot be created on the stack or as temporaries!" );
#endif
m_IsBeingDeleted = false;
}
IDeletableObject::~IDeletableObject() throw()
BaseDeletableObject::~BaseDeletableObject() throw()
{
AffinityAssert_AllowFromMain();
AffinityAssert_AllowFrom_MainUI();
}
@ -334,7 +336,6 @@ pxStaticHeading* wxPanelWithHelpers::Heading( const wxString& label )
return new pxStaticHeading( this, label );
}
wxPanelWithHelpers::wxPanelWithHelpers( wxWindow* parent, wxOrientation orient, const wxString& staticBoxLabel )
: wxPanel( parent )
{

View File

@ -12,6 +12,6 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="w32pthreads.v3.lib w32pthreads_lib.lib"
AdditionalDependencies="w32pthreads_lib.lib"
/>
</VisualStudioPropertySheet>

View File

@ -93,7 +93,7 @@ FILE *_cdvdOpenMechaVer()
if (fd == NULL)
{
Console.Error( "MEC File Creation failed!" );
throw Exception::CreateStream( file );
throw Exception::CannotCreateStream( file );
//Msgbox::Alert( "_cdvdOpenMechaVer: Error creating %s", file);
//exit(1);
}
@ -133,7 +133,7 @@ FILE *_cdvdOpenNVM()
if (fd == NULL)
{
Console.Error( "NVM File Creation failed!" );
throw Exception::CreateStream( file );
throw Exception::CannotCreateStream( file );
}
for (int i=0; i<1024; i++) fputc(0, fd);
}
@ -305,7 +305,7 @@ s32 cdvdWriteConfig(const u8* config)
}
}
static MutexLockRecursive Mutex_NewDiskCB;
static MutexRecursive Mutex_NewDiskCB;
// Sets ElfCRC to the CRC of the game bound to the CDVD plugin.
static __forceinline ElfObject *loadElf( const wxString filename )
@ -566,11 +566,11 @@ static void cdvdDetectDisk()
void cdvdNewDiskCB()
{
if( !Mutex_NewDiskCB.TryAcquire() ) return;
ScopedTryLock lock( Mutex_NewDiskCB );
if( lock.Failed() ) return;
DoCDVDresetDiskTypeCache();
try { cdvdDetectDisk(); }
catch(...) { Mutex_NewDiskCB.Release(); } // ensure mutex gets unlocked.
cdvdDetectDisk();
}
static void mechaDecryptBytes( u32 madr, int size )

View File

@ -288,7 +288,7 @@ CDVD_SourceType CDVDsys_GetSourceType()
void CDVDsys_ChangeSource( CDVD_SourceType type )
{
GetPluginManager().Close( PluginId_CDVD );
GetCorePlugins().Close( PluginId_CDVD );
switch( m_CurrentSourceType = type )
{

View File

@ -94,7 +94,7 @@ IsoDirectory::IsoDirectory(SectorSource& r)
}
if( !isValid )
throw Exception::BadStream( "IsoFS", "Root directory not found on ISO image." );
throw Exception::FileNotFound( "IsoFS", "Root directory not found on ISO image." );
DevCon.WriteLn( L"(IsoFS) Filesystem is " + FStype_ToString() );
Init( rootDirEntry );

View File

@ -484,8 +484,8 @@ struct Pcsx2Config
CdvdDumpBlocks :1, // enables cdvd block dumping
EnablePatches :1, // enables patch detection and application
// when enabled performs bios stub execution, skipping full sony bios + splash screens
SkipBiosSplash :1,
// when enabled uses BOOT2 injection, skipping sony bios splashes
UseBOOT2Injection :1,
// enables simulated ejection of memory cards when loading savestates
McdEnableEjection :1,

View File

@ -222,8 +222,7 @@ static void vSyncInfoCalc( vSyncTimingInfo* info, Fixed100 framesPerSecond, u32
u32 UpdateVSyncRate()
{
XMMRegisters::Freeze();
MMXRegisters::Freeze();
Registers::Freeze();
// Notice: (and I probably repeat this elsewhere, but it's worth repeating)
// The PS2's vsync timer is an *independent* crystal that is fixed to either 59.94 (NTSC)
@ -234,8 +233,8 @@ u32 UpdateVSyncRate()
// 1/5 and 4/5 ratios).
Fixed100 framerate;
u32 scanlines;
bool isCustom;
u32 scanlines;
bool isCustom;
if( gsRegionMode == Region_PAL )
{
@ -278,8 +277,7 @@ u32 UpdateVSyncRate()
m_iStart = GetCPUTicks();
XMMRegisters::Thaw();
MMXRegisters::Thaw();
Registers::Thaw();
return (u32)m_iTicks;
}

View File

@ -118,8 +118,8 @@ void LoadGSState(const wxString& file)
RunGSState( f );
g_plugins->Close( PluginId_GS );
g_plugins->Close( PluginId_PAD );
GetCorePlugins().Close( PluginId_GS );
GetCorePlugins().Close( PluginId_PAD );
}
struct GSStatePacket

View File

@ -287,7 +287,6 @@
<Unit filename="../R5900OpcodeImpl.cpp" />
<Unit filename="../R5900OpcodeTables.cpp" />
<Unit filename="../R5900OpcodeTables.h" />
<Unit filename="../RecoverySystem.cpp" />
<Unit filename="../SPR.cpp" />
<Unit filename="../SPR.h" />
<Unit filename="../SamplProf.h" />
@ -338,6 +337,9 @@
<Unit filename="../Vif_Unpack.cpp" />
<Unit filename="../Vif_Unpack.h" />
<Unit filename="../Vif_Unpack.inl" />
<Unit filename="../ZipTools/ThreadedZipTools.h" />
<Unit filename="../ZipTools/thread_gzip.cpp" />
<Unit filename="../ZipTools/thread_lzma.cpp" />
<Unit filename="../gui/AdvancedDialog.cpp" />
<Unit filename="../gui/AdvancedDialog.h" />
<Unit filename="../gui/App.h" />
@ -345,6 +347,8 @@
<Unit filename="../gui/AppConfig.cpp" />
<Unit filename="../gui/AppConfig.h" />
<Unit filename="../gui/AppCoreThread.cpp" />
<Unit filename="../gui/AppCorePlugins.cpp" />
<Unit filename="../gui/AppCoreThread.h" />
<Unit filename="../gui/AppEventListeners.h" />
<Unit filename="../gui/AppEventSources.cpp" />
<Unit filename="../gui/AppForwardDefs.h" />
@ -376,6 +380,7 @@
<Unit filename="../gui/Dialogs/PickUserModeDialog.cpp" />
<Unit filename="../gui/Dialogs/StuckThreadDialog.cpp" />
<Unit filename="../gui/Dialogs/SysConfigDialog.cpp" />
<Unit filename="../gui/ExecutorThread.h" />
<Unit filename="../gui/FrameForGS.cpp" />
<Unit filename="../gui/GlobalCommands.cpp" />
<Unit filename="../gui/IniInterface.cpp" />
@ -408,7 +413,6 @@
<Unit filename="../gui/Panels/PluginSelectorPanel.cpp" />
<Unit filename="../gui/Panels/SpeedhacksPanel.cpp" />
<Unit filename="../gui/Panels/VideoPanel.cpp" />
<Unit filename="../gui/Plugins.cpp" />
<Unit filename="../gui/RecentIsoList.cpp" />
<Unit filename="../gui/RecentIsoList.h" />
<Unit filename="../gui/Resources/AppIcon16.h" />
@ -491,9 +495,13 @@
</Unit>
<Unit filename="../gui/Resources/EmbeddedImage.h" />
<Unit filename="../gui/Saveslots.cpp" />
<Unit filename="../gui/SysState.cpp" />
<Unit filename="../gui/UpdateUI.cpp" />
<Unit filename="../gui/i18n.cpp" />
<Unit filename="../gui/i18n.h" />
<Unit filename="../gui/gsFrame.h" />
<Unit filename="../gui/pxLogTextCtrl.cpp" />
<Unit filename="../gui/pxEvtThread.h" />
<Unit filename="../pcsx2hostfs.cpp" />
<Unit filename="../ps2/BiosTools.cpp" />
<Unit filename="../ps2/BiosTools.h" />

View File

@ -399,9 +399,9 @@ void SysMtgsThread::ExecuteTaskInThread()
{
MTGS_FreezeData* data = (MTGS_FreezeData*)(*(uptr*)&tag.data[1]);
int mode = tag.data[0];
data->retval = GetPluginManager().DoFreeze( PluginId_GS, mode, data->fdata );
break;
data->retval = GetCorePlugins().DoFreeze( PluginId_GS, mode, data->fdata );
}
break;
case GS_RINGTYPE_RECORD:
{
@ -488,8 +488,7 @@ void SysMtgsThread::ClosePlugin()
{
if( !m_PluginOpened ) return;
m_PluginOpened = false;
if( g_plugins != NULL )
g_plugins->m_info[PluginId_GS].CommonBindings.Close();
GetCorePlugins().Close( PluginId_GS );
}
void SysMtgsThread::OnSuspendInThread()
@ -998,7 +997,7 @@ void SysMtgsThread::WaitForOpen()
void SysMtgsThread::Freeze( int mode, MTGS_FreezeData& data )
{
GetPluginManager().Open( PluginId_GS );
GetCorePlugins().Open( PluginId_GS );
SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &data );
Resume();
WaitGS();

View File

@ -42,7 +42,6 @@ namespace PathDefs
// complete pathnames are returned by these functions
// For 99% of all code, you should use these.
extern wxDirName GetDocuments();
extern wxDirName GetSnapshots();
extern wxDirName GetBios();
extern wxDirName GetThemes();
@ -51,7 +50,6 @@ namespace PathDefs
extern wxDirName GetMemoryCards();
extern wxDirName GetSettings();
extern wxDirName GetLogs();
extern wxDirName GetThemes();
extern wxDirName Get( FoldersEnum_t folderidx );

View File

@ -275,7 +275,7 @@ void Pcsx2Config::LoadSave( IniInterface& ini )
IniBitBool( EnablePatches );
IniBitBool( ConsoleToStdio );
IniBitBool( SkipBiosSplash );
IniBitBool( UseBOOT2Injection );
IniBitBool( McdEnableEjection );
IniBitBool( MultitapPort0_Enabled );

View File

@ -708,88 +708,193 @@ static void PS2E_CALLBACK pcsx2_OSD_WriteLn( int icon, const char* msg )
}
// ---------------------------------------------------------------------------------
// Plugin Manager Implementation
// PluginStatus_t Implementations
// ---------------------------------------------------------------------------------
PluginManager::PluginManager( const wxString (&folders)[PluginId_Count] )
PluginManager::PluginStatus_t::PluginStatus_t( PluginsEnum_t _pid, const wxString& srcfile )
: Filename( srcfile )
{
pid = _pid;
IsInitialized = false;
IsOpened = false;
if( Filename.IsEmpty() )
throw Exception::PluginInitError( pid, "Empty plugin filename." );
if( !wxFile::Exists( Filename ) )
throw Exception::PluginLoadError( pid, srcfile,
wxLt("The configured %s plugin file was not found")
);
if( !Lib.Load( Filename ) )
throw Exception::PluginLoadError( pid, Filename,
wxLt("The configured %s plugin file is not a valid dynamic library")
);
// Try to enumerate the new v2.0 plugin interface first.
// If that fails, fall back on the old style interface.
//m_libs[i].GetSymbol( L"PS2E_InitAPI" ); // on the TODO list!
// 2.0 API Failed; Enumerate the Old Stuff! -->
_PS2EgetLibName GetLibName = (_PS2EgetLibName) Lib.GetSymbol( L"PS2EgetLibName" );
_PS2EgetLibVersion2 GetLibVersion2 = (_PS2EgetLibVersion2) Lib.GetSymbol( L"PS2EgetLibVersion2" );
_PS2EsetEmuVersion SetEmuVersion = (_PS2EsetEmuVersion) Lib.GetSymbol( L"PS2EsetEmuVersion" );
if( GetLibName == NULL || GetLibVersion2 == NULL )
throw Exception::PluginLoadError( pid, Filename,
L"\nMethod binding failure on GetLibName or GetLibVersion2.\n",
_( "Configured plugin is not a PCSX2 plugin, or is for an older unsupported version of PCSX2." )
);
if( SetEmuVersion != NULL )
SetEmuVersion( "PCSX2", (0ul << 24) | (9ul<<16) | (7ul<<8) | 0 );
Name = fromUTF8( GetLibName() );
int version = GetLibVersion2( tbl_PluginInfo[pid].typemask );
Version.Printf( L"%d.%d.%d", (version>>8)&0xff, version&0xff, (version>>24)&0xff );
// Bind Required Functions
// (generate critical error if binding fails)
BindCommon( pid );
BindRequired( pid );
BindOptional( pid );
// Run Plugin's Functionality Test.
// A lot of plugins don't bother to implement this function and return 0 (success)
// regardless, but some do so let's go ahead and check it. I mean, we're supposed to. :)
int testres = CommonBindings.Test();
if( testres != 0 )
throw Exception::PluginLoadError( pid, Filename,
wxsFormat( L"Plugin Test failure, return code: %d", testres ),
_( "The plugin reports that your hardware or software/drivers are not supported." )
);
}
void PluginManager::PluginStatus_t::BindCommon( PluginsEnum_t pid )
{
const LegacyApi_CommonMethod* current = s_MethMessCommon;
VoidMethod** target = (VoidMethod**)&CommonBindings;
wxDoNotLogInThisScope please;
Console.WriteLn( Color_StrongBlue, "Loading plugins..." );
const PluginInfo* pi = tbl_PluginInfo; do
while( current->MethodName != NULL )
{
ConsoleIndentScope indent;
const PluginsEnum_t pid = pi->id;
*target = (VoidMethod*)Lib.GetSymbol( current->GetMethodName( pid ) );
Console.WriteLn( L"Binding %s\t: %s ", tbl_PluginInfo[pid].GetShortname().c_str(), folders[pid].c_str() );
if( *target == NULL )
*target = current->Fallback;
if( folders[pid].IsEmpty() )
throw Exception::PluginInitError( pi->id, "Empty plugin filename." );
m_info[pid].Filename = folders[pid];
if( !wxFile::Exists( folders[pid] ) )
throw Exception::PluginLoadError( pid, folders[pid],
wxLt("The configured %s plugin file was not found")
);
if( !m_info[pid].Lib.Load( folders[pid] ) )
throw Exception::PluginLoadError( pid, folders[pid],
wxLt("The configured %s plugin file is not a valid dynamic library")
);
// Try to enumerate the new v2.0 plugin interface first.
// If that fails, fall back on the old style interface.
//m_libs[i].GetSymbol( L"PS2E_InitAPI" );
// Fetch plugin name and version information
_PS2EgetLibName GetLibName = (_PS2EgetLibName) m_info[pid].Lib.GetSymbol( L"PS2EgetLibName" );
_PS2EgetLibVersion2 GetLibVersion2 = (_PS2EgetLibVersion2) m_info[pid].Lib.GetSymbol( L"PS2EgetLibVersion2" );
_PS2EsetEmuVersion SetEmuVersion = (_PS2EsetEmuVersion) m_info[pid].Lib.GetSymbol( L"PS2EsetEmuVersion" );
if( GetLibName == NULL || GetLibVersion2 == NULL )
throw Exception::PluginLoadError( pid, m_info[pid].Filename,
L"\nMethod binding failure on GetLibName or GetLibVersion2.\n",
if( *target == NULL )
{
throw Exception::PluginLoadError( pid, Filename,
wxsFormat( L"\nMethod binding failure on: %s\n", current->GetMethodName( pid ).c_str() ),
_( "Configured plugin is not a PCSX2 plugin, or is for an older unsupported version of PCSX2." )
);
}
if( SetEmuVersion != NULL )
SetEmuVersion( "PCSX2", (0ul << 24) | (9ul<<16) | (7ul<<8) | 0 );
target++;
current++;
}
}
m_info[pid].Name = fromUTF8( GetLibName() );
int version = GetLibVersion2( tbl_PluginInfo[pid].typemask );
m_info[pid].Version.Printf( L"%d.%d.%d", (version>>8)&0xff, version&0xff, (version>>24)&0xff );
void PluginManager::PluginStatus_t::BindRequired( PluginsEnum_t pid )
{
const LegacyApi_ReqMethod* current = s_MethMessReq[pid];
const wxDynamicLibrary& lib = Lib;
// Bind Required Functions
// (generate critical error if binding fails)
wxDoNotLogInThisScope please;
BindCommon( pid );
BindRequired( pid );
BindOptional( pid );
while( current->MethodName != NULL )
{
*(current->Dest) = (VoidMethod*)lib.GetSymbol( current->GetMethodName() );
// Run Plugin's Functionality Test.
// A lot of plugins don't bother to implement this function and return 0 (success)
// regardless, but some do so let's go ahead and check it. I mean, we're supposed to. :)
if( *(current->Dest) == NULL )
*(current->Dest) = current->Fallback;
int testres = m_info[pi->id].CommonBindings.Test();
if( testres != 0 )
throw Exception::PluginLoadError( pid, m_info[pid].Filename,
wxsFormat( L"Plugin Test failure, return code: %d", testres ),
_( "The plugin reports that your hardware or software/drivers are not supported." )
if( *(current->Dest) == NULL )
{
throw Exception::PluginLoadError( pid, Filename,
wxsFormat( L"\nMethod binding failure on: %s\n", current->GetMethodName().c_str() ),
_( "Configured plugin is not a valid PCSX2 plugin, or is for an older unsupported version of PCSX2." )
);
}
current++;
}
}
void PluginManager::PluginStatus_t::BindOptional( PluginsEnum_t pid )
{
const LegacyApi_OptMethod* current = s_MethMessOpt[pid];
const wxDynamicLibrary& lib = Lib;
wxDoNotLogInThisScope please;
while( current->MethodName != NULL )
{
*(current->Dest) = (VoidMethod*)lib.GetSymbol( current->GetMethodName() );
current++;
}
}
// =====================================================================================
// PluginManager Implementations
// =====================================================================================
PluginManager::PluginManager()
{
}
PluginManager::~PluginManager() throw()
{
try
{
Unload();
}
DESTRUCTOR_CATCHALL
// All library unloading done automatically by wx.
}
void PluginManager::Load( PluginsEnum_t pid, const wxString& srcfile )
{
ScopedLock lock( m_mtx_PluginStatus );
pxAssume( (uint)pid < PluginId_Count );
Console.WriteLn( L"Binding %s\t: %s ", tbl_PluginInfo[pid].GetShortname().c_str(), srcfile.c_str() );
m_info[pid] = new PluginStatus_t( pid, srcfile );
}
void PluginManager::Load( const wxString (&folders)[PluginId_Count] )
{
ScopedLock lock( m_mtx_PluginStatus );
if( !NeedsLoad() ) return;
wxDoNotLogInThisScope please;
Console.WriteLn( Color_StrongBlue, "Loading plugins..." );
ConsoleIndentScope indent;
const PluginInfo* pi = tbl_PluginInfo; do
{
Load( pi->id, folders[pi->id] );
pxYield( 2 );
} while( ++pi, pi->shortname != NULL );
indent.EndScope();
CDVDapi_Plugin.newDiskCB( cdvdNewDiskCB );
// Hack for PAD's stupid parameter passed on Init
PADinit = (_PADinit)m_info[PluginId_PAD].CommonBindings.Init;
m_info[PluginId_PAD].CommonBindings.Init = _hack_PADinit;
PADinit = (_PADinit)m_info[PluginId_PAD]->CommonBindings.Init;
m_info[PluginId_PAD]->CommonBindings.Init = _hack_PADinit;
Console.WriteLn( Color_StrongBlue, "Plugins loaded successfully.\n" );
@ -822,90 +927,34 @@ PluginManager::PluginManager( const wxString (&folders)[PluginId_Count] )
throw Exception::PluginLoadError( PluginId_Mcd, wxEmptyString, "Internal Memorycard Plugin failed to load." );
}
g_plugins = this;
SendSettingsFolder();
}
PluginManager::~PluginManager() throw()
void PluginManager::Unload(PluginsEnum_t pid)
{
try
{
Close();
Shutdown();
}
DESTRUCTOR_CATCHALL
// All library unloading done automatically.
if( g_plugins == this )
g_plugins = NULL;
ScopedLock lock( m_mtx_PluginStatus );
pxAssume( (uint)pid < PluginId_Count );
m_info[pid].Delete();
}
void PluginManager::BindCommon( PluginsEnum_t pid )
void PluginManager::Unload()
{
const LegacyApi_CommonMethod* current = s_MethMessCommon;
VoidMethod** target = (VoidMethod**)&m_info[pid].CommonBindings;
ScopedLock lock( m_mtx_PluginStatus );
wxDoNotLogInThisScope please;
if( NeedsShutdown() )
Console.Warning( "(SysCorePlugins) Warning: Unloading plugins prior to shutdown!" );
while( current->MethodName != NULL )
{
*target = (VoidMethod*)m_info[pid].Lib.GetSymbol( current->GetMethodName( pid ) );
//Shutdown();
if( *target == NULL )
*target = current->Fallback;
if( !NeedsUnload() ) return;
if( *target == NULL )
{
throw Exception::PluginLoadError( pid, m_info[pid].Filename,
wxsFormat( L"\nMethod binding failure on: %s\n", current->GetMethodName( pid ).c_str() ),
_( "Configured plugin is not a PCSX2 plugin, or is for an older unsupported version of PCSX2." )
);
}
DbgCon.WriteLn( Color_StrongBlue, "Unloading plugins..." );
target++;
current++;
}
}
for( int i=PluginId_Count-1; i>=0; --i )
Unload( tbl_PluginInfo[i].id );
void PluginManager::BindRequired( PluginsEnum_t pid )
{
const LegacyApi_ReqMethod* current = s_MethMessReq[pid];
const wxDynamicLibrary& lib = m_info[pid].Lib;
DbgCon.WriteLn( Color_StrongBlue, "Plugins unloaded successfully." );
wxDoNotLogInThisScope please;
while( current->MethodName != NULL )
{
*(current->Dest) = (VoidMethod*)lib.GetSymbol( current->GetMethodName() );
if( *(current->Dest) == NULL )
*(current->Dest) = current->Fallback;
if( *(current->Dest) == NULL )
{
throw Exception::PluginLoadError( pid, m_info[pid].Filename,
wxsFormat( L"\nMethod binding failure on: %s\n", current->GetMethodName().c_str() ),
_( "Configured plugin is not a valid PCSX2 plugin, or is for an older unsupported version of PCSX2." )
);
}
current++;
}
}
void PluginManager::BindOptional( PluginsEnum_t pid )
{
const LegacyApi_OptMethod* current = s_MethMessOpt[pid];
const wxDynamicLibrary& lib = m_info[pid].Lib;
wxDoNotLogInThisScope please;
while( current->MethodName != NULL )
{
*(current->Dest) = (VoidMethod*)lib.GetSymbol( current->GetMethodName() );
current++;
}
}
// Exceptions:
@ -978,7 +1027,8 @@ bool PluginManager::OpenPlugin_FW()
void PluginManager::Open( PluginsEnum_t pid )
{
if( m_info[pid].IsOpened ) return;
pxAssume( (uint)pid < PluginId_Count );
if( IsOpen(pid) ) return;
Console.Indent().WriteLn( "Opening %s", tbl_PluginInfo[pid].shortname );
@ -999,29 +1049,14 @@ void PluginManager::Open( PluginsEnum_t pid )
if( !result )
throw Exception::PluginOpenError( pid );
m_info[pid].IsOpened = true;
}
bool PluginManager::NeedsOpen() const
{
const PluginInfo* pi = tbl_PluginInfo; do {
if( !m_info[pi->id].IsOpened ) break;
} while( ++pi, pi->shortname != NULL );
return pi->shortname != NULL;
}
bool PluginManager::NeedsClose() const
{
const PluginInfo* pi = tbl_PluginInfo; do {
if( m_info[pi->id].IsOpened ) break;
} while( ++pi, pi->shortname != NULL );
return pi->shortname != NULL;
ScopedLock lock( m_mtx_PluginStatus );
if( m_info[pid] ) m_info[pid]->IsOpened = true;
}
void PluginManager::Open()
{
Init();
if( !NeedsOpen() ) return; // Spam stopper: returns before writing any logs. >_<
Console.WriteLn( Color_StrongBlue, "Opening plugins..." );
@ -1041,12 +1076,23 @@ void PluginManager::Open()
Console.WriteLn( Color_StrongBlue, "Plugins opened successfully." );
}
void PluginManager::_generalclose( PluginsEnum_t pid )
{
ScopedLock lock( m_mtx_PluginStatus );
if( m_info[pid] ) m_info[pid]->CommonBindings.Close();
}
void PluginManager::ClosePlugin_GS()
{
// force-close PAD before GS, because the PAD depends on the GS window.
// old-skool: force-close PAD before GS, because the PAD depends on the GS window.
Close( PluginId_PAD );
GetMTGS().Suspend();
if( GetMTGS().IsSelf() )
_generalclose( PluginId_GS );
else
{
if( !GSopen2 ) Close( PluginId_PAD );
GetMTGS().Suspend();
}
}
void PluginManager::ClosePlugin_CDVD()
@ -1056,33 +1102,35 @@ void PluginManager::ClosePlugin_CDVD()
void PluginManager::ClosePlugin_PAD()
{
m_info[PluginId_PAD].CommonBindings.Close();
_generalclose( PluginId_PAD );
}
void PluginManager::ClosePlugin_SPU2()
{
m_info[PluginId_SPU2].CommonBindings.Close();
_generalclose( PluginId_SPU2 );
}
void PluginManager::ClosePlugin_DEV9()
{
m_info[PluginId_DEV9].CommonBindings.Close();
_generalclose( PluginId_DEV9 );
}
void PluginManager::ClosePlugin_USB()
{
m_info[PluginId_USB].CommonBindings.Close();
_generalclose( PluginId_USB );
}
void PluginManager::ClosePlugin_FW()
{
m_info[PluginId_FW].CommonBindings.Close();
_generalclose( PluginId_FW );
}
void PluginManager::Close( PluginsEnum_t pid )
{
if( !m_info[pid].IsOpened ) return;
pxAssume( (uint)pid < PluginId_Count );
if( !IsOpen(pid) ) return;
Console.Indent().WriteLn( "Closing %s", tbl_PluginInfo[pid].shortname );
switch( pid )
@ -1094,11 +1142,12 @@ void PluginManager::Close( PluginsEnum_t pid )
case PluginId_USB: ClosePlugin_USB(); break;
case PluginId_FW: ClosePlugin_FW(); break;
case PluginId_DEV9: ClosePlugin_DEV9(); break;
jNO_DEFAULT;
}
m_info[pid].IsOpened = false;
ScopedLock lock( m_mtx_PluginStatus );
if( m_info[pid] ) m_info[pid]->IsOpened = false;
}
void PluginManager::Close()
@ -1126,22 +1175,26 @@ void PluginManager::Close()
//
void PluginManager::Init()
{
ScopedLock lock( m_mtx_PluginStatus );
if( !NeedsInit() ) return;
bool printlog = false;
const PluginInfo* pi = tbl_PluginInfo; do
{
const PluginsEnum_t pid = pi->id;
if( m_info[pid].IsInitialized ) continue;
if( !m_info[pid] || m_info[pid]->IsInitialized ) continue;
if( !printlog )
{
Console.WriteLn( Color_StrongBlue, "Initializing plugins..." );
printlog = true;
}
Console.Indent().WriteLn( "Init %s", tbl_PluginInfo[pid].shortname );
if( 0 != m_info[pid].CommonBindings.Init() )
if( 0 != m_info[pid]->CommonBindings.Init() )
throw Exception::PluginInitError( pid );
m_info[pid].IsInitialized = true;
m_info[pid]->IsInitialized = true;
} while( ++pi, pi->shortname != NULL );
if( SysPlugins.Mcd == NULL )
@ -1166,9 +1219,13 @@ void PluginManager::Init()
//
void PluginManager::Shutdown()
{
GetMTGS().Cancel(); // cancel it for speedier shutdown!
ScopedLock lock( m_mtx_PluginStatus );
if( !NeedsShutdown() ) return;
Close();
pxAssumeDev( !NeedsClose(), "Cannot shut down plugins prior to Close()" );
GetMTGS().Cancel(); // cancel it for speedier shutdown!
DbgCon.WriteLn( Color_StrongGreen, "Shutting down plugins..." );
// Shutdown plugins in reverse order (probably doesn't matter...
@ -1177,10 +1234,10 @@ void PluginManager::Shutdown()
for( int i=PluginId_Count-1; i>=0; --i )
{
const PluginsEnum_t pid = tbl_PluginInfo[i].id;
if( !m_info[pid].IsInitialized ) continue;
if( !m_info[pid] || !m_info[pid]->IsInitialized ) continue;
DevCon.Indent().WriteLn( "Shutdown %s", tbl_PluginInfo[pid].shortname );
m_info[pid].IsInitialized = false;
m_info[pid].CommonBindings.Shutdown();
m_info[pid]->IsInitialized = false;
m_info[pid]->CommonBindings.Shutdown();
}
// More memorycard hacks!!
@ -1208,7 +1265,8 @@ bool PluginManager::DoFreeze( PluginsEnum_t pid, int mode, freezeData* data )
}
else
{
return m_info[pid].CommonBindings.Freeze( mode, data ) != -1;
ScopedLock lock( m_mtx_PluginStatus );
return !m_info[pid] || m_info[pid]->CommonBindings.Freeze( mode, data ) != -1;
}
}
@ -1219,6 +1277,9 @@ bool PluginManager::DoFreeze( PluginsEnum_t pid, int mode, freezeData* data )
//
void PluginManager::Freeze( PluginsEnum_t pid, SaveStateBase& state )
{
// No locking leeded -- DoFreeze locks as needed, and this avoids MTGS deadlock.
//ScopedLock lock( m_mtx_PluginStatus );
Console.Indent().WriteLn( "%s %s", state.IsSaving() ? "Saving" : "Loading",
tbl_PluginInfo[pid].shortname );
@ -1265,14 +1326,16 @@ void PluginManager::Freeze( PluginsEnum_t pid, SaveStateBase& state )
bool PluginManager::KeyEvent( const keyEvent& evt )
{
ScopedLock lock( m_mtx_PluginStatus );
// [TODO] : The plan here is to give plugins "first chance" handling of keys.
// Handling order will be fixed (GS, SPU2, PAD, etc), and the first plugin to
// pick up the key and return "true" (for handled) will cause the loop to break.
// The current version of PS2E doesn't support it yet, though.
const PluginInfo* pi = tbl_PluginInfo; do {
if( pi->id != PluginId_PAD )
m_info[pi->id].CommonBindings.KeyEvent( const_cast<keyEvent*>(&evt) );
if( pi->id != PluginId_PAD && m_info[pi->id] )
m_info[pi->id]->CommonBindings.KeyEvent( const_cast<keyEvent*>(&evt) );
} while( ++pi, pi->shortname != NULL );
return false;
@ -1280,23 +1343,26 @@ bool PluginManager::KeyEvent( const keyEvent& evt )
void PluginManager::SendSettingsFolder()
{
ScopedLock lock( m_mtx_PluginStatus );
if( m_SettingsFolder.IsEmpty() ) return;
wxCharBuffer utf8buffer( m_SettingsFolder.ToUTF8() );
const PluginInfo* pi = tbl_PluginInfo; do {
m_info[pi->id].CommonBindings.SetSettingsDir( utf8buffer );
if( m_info[pi->id] ) m_info[pi->id]->CommonBindings.SetSettingsDir( utf8buffer );
} while( ++pi, pi->shortname != NULL );
}
void PluginManager::SetSettingsFolder( const wxString& folder )
{
ScopedLock lock( m_mtx_PluginStatus );
wxString fixedfolder( folder );
if( !fixedfolder.IsEmpty() && (fixedfolder[fixedfolder.length()-1] != wxFileName::GetPathSeparator() ) )
{
fixedfolder += wxFileName::GetPathSeparator();
}
if( m_SettingsFolder == fixedfolder ) return;
m_SettingsFolder = fixedfolder;
SendSettingsFolder();
@ -1304,26 +1370,126 @@ void PluginManager::SetSettingsFolder( const wxString& folder )
void PluginManager::Configure( PluginsEnum_t pid )
{
m_info[pid].CommonBindings.Configure();
ScopedLock lock( m_mtx_PluginStatus );
if( m_info[pid] ) m_info[pid]->CommonBindings.Configure();
}
PluginManager* PluginManager_Create( const wxChar* (&folders)[PluginId_Count] )
bool PluginManager::AreLoaded() const
{
wxString passins[PluginId_Count];
ScopedLock lock( m_mtx_PluginStatus );
for( int i=0; i<PluginId_Count; ++i )
{
if( !m_info[i] ) return false;
}
return true;
}
bool PluginManager::AreAnyLoaded() const
{
ScopedLock lock( m_mtx_PluginStatus );
for( int i=0; i<PluginId_Count; ++i )
{
if( m_info[i] ) return true;
}
return false;
}
bool PluginManager::AreAnyInitialized() const
{
ScopedLock lock( m_mtx_PluginStatus );
const PluginInfo* pi = tbl_PluginInfo; do {
passins[pi->id] = folders[pi->id];
if( IsInitialized(pi->id) ) return true;
} while( ++pi, pi->shortname != NULL );
return new PluginManager( passins );
return false;
}
static PluginManagerBase s_pluginman_placebo;
// retrieves a handle to the current plugin manager. Plugin manager is assumed to be valid,
// and debug-level assertions are performed on the validity of the handle.
PluginManagerBase& GetPluginManager()
bool PluginManager::IsOpen( PluginsEnum_t pid ) const
{
if( g_plugins == NULL ) return s_pluginman_placebo;
return *g_plugins;
pxAssume( (uint)pid < PluginId_Count );
ScopedLock lock( m_mtx_PluginStatus );
return m_info[pid] && m_info[pid]->IsOpened;
}
bool PluginManager::IsInitialized( PluginsEnum_t pid ) const
{
pxAssume( (uint)pid < PluginId_Count );
ScopedLock lock( m_mtx_PluginStatus );
return m_info[pid] && m_info[pid]->IsInitialized;
}
bool PluginManager::IsLoaded( PluginsEnum_t pid ) const
{
pxAssume( (uint)pid < PluginId_Count );
return !!m_info[pid];
}
bool PluginManager::NeedsLoad() const
{
const PluginInfo* pi = tbl_PluginInfo; do {
if( !IsLoaded(pi->id) ) return true;
} while( ++pi, pi->shortname != NULL );
return false;
}
bool PluginManager::NeedsUnload() const
{
const PluginInfo* pi = tbl_PluginInfo; do {
if( IsLoaded(pi->id) ) return true;
} while( ++pi, pi->shortname != NULL );
return false;
}
bool PluginManager::NeedsInit() const
{
const PluginInfo* pi = tbl_PluginInfo; do {
if( !IsInitialized(pi->id) ) return true;
} while( ++pi, pi->shortname != NULL );
return false;
}
bool PluginManager::NeedsShutdown() const
{
const PluginInfo* pi = tbl_PluginInfo; do {
if( IsInitialized(pi->id) ) return true;
} while( ++pi, pi->shortname != NULL );
return false;
}
bool PluginManager::NeedsOpen() const
{
const PluginInfo* pi = tbl_PluginInfo; do {
if( !IsOpen(pi->id) ) return true;
} while( ++pi, pi->shortname != NULL );
return false;
}
bool PluginManager::NeedsClose() const
{
const PluginInfo* pi = tbl_PluginInfo; do {
if( IsOpen(pi->id) ) return true;
} while( ++pi, pi->shortname != NULL );
return false;
}
const wxString PluginManager::GetName( PluginsEnum_t pid ) const
{
ScopedLock lock( m_mtx_PluginStatus );
pxAssume( (uint)pid < PluginId_Count );
return m_info[pid] ? m_info[pid]->Name : _("Unloaded Plugin");
}
const wxString PluginManager::GetVersion( PluginsEnum_t pid ) const
{
ScopedLock lock( m_mtx_PluginStatus );
pxAssume( (uint)pid < PluginId_Count );
return m_info[pid] ? m_info[pid]->Version : _("0.0");
}

View File

@ -21,6 +21,8 @@
#include "PS2Edefs.h"
#include "PluginCallbacks.h"
#include "Utilities/Threading.h"
#include <wx/dynlib.h>
#ifdef _MSC_VER
@ -125,7 +127,7 @@ namespace Exception
PluginId = pid;
}
};
// This exception is thrown when a plugin returns an error while trying to save itself.
// Typically this should be a very rare occurance since a plugin typically shoudn't
// be doing memory allocations or file access during state saving.
@ -222,57 +224,20 @@ public:
extern SysPluginBindings SysPlugins;
// --------------------------------------------------------------------------------------
// PluginManagerBase Class
// --------------------------------------------------------------------------------------
// Provides a basic placebo "do-nothing" interface for plugin management. This is used
// to avoid NULL pointer exceptions/segfaults when referencing the plugin manager global
// handle.
//
// Note: The Init and Freeze methods of this class will cause debug assertions, but Close
// methods fail silently, on the premise that Close and Shutdown are typically run from
// exception handlers or cleanup code, and null pointers should be silently ignored in
// favor of continuing cleanup.
//
class PluginManagerBase
{
DeclareNoncopyableObject( PluginManagerBase );
public:
PluginManagerBase() {}
virtual ~PluginManagerBase() {}
virtual void Init() { pxFail( "Null PluginManager!" ); }
virtual void Shutdown() {}
virtual void Open() { }
virtual void Open( PluginsEnum_t pid ) { pxFail( "Null PluginManager!" ); }
virtual void Close( PluginsEnum_t pid ) {}
virtual void Close() {}
virtual bool IsOpen( PluginsEnum_t pid ) const { return false; }
virtual void Freeze( PluginsEnum_t pid, SaveStateBase& state ) { pxFail( "Null PluginManager!" ); }
virtual bool DoFreeze( PluginsEnum_t pid, int mode, freezeData* data )
{
pxFail( "Null PluginManager!" );
return false;
}
virtual bool KeyEvent( const keyEvent& evt ) { return false; }
};
// --------------------------------------------------------------------------------------
// PluginManager Class
// --------------------------------------------------------------------------------------
//
class PluginManager : public PluginManagerBase
class PluginManager
{
DeclareNoncopyableObject( PluginManager );
protected:
struct PluginStatus_t
class PluginStatus_t
{
public:
PluginsEnum_t pid;
bool IsInitialized;
bool IsOpened;
@ -283,23 +248,44 @@ protected:
LegacyPluginAPI_Common CommonBindings;
wxDynamicLibrary Lib;
public:
PluginStatus_t()
{
IsInitialized = false;
IsOpened = false;
}
PluginStatus_t( PluginsEnum_t _pid, const wxString& srcfile );
virtual ~PluginStatus_t() throw() { }
protected:
void BindCommon( PluginsEnum_t pid );
void BindRequired( PluginsEnum_t pid );
void BindOptional( PluginsEnum_t pid );
};
const PS2E_LibraryAPI* m_mcdPlugin;
wxString m_SettingsFolder;
const PS2E_LibraryAPI* m_mcdPlugin;
wxString m_SettingsFolder;
Threading::MutexRecursive m_mtx_PluginStatus;
public: // hack until we unsuck plugins...
PluginStatus_t m_info[PluginId_Count];
ScopedPtr<PluginStatus_t> m_info[PluginId_Count];
public:
PluginManager( const wxString (&folders)[PluginId_Count] );
PluginManager();
virtual ~PluginManager() throw();
void Load( PluginsEnum_t pid, const wxString& srcfile );
void Load( const wxString (&folders)[PluginId_Count] );
void Unload();
void Unload( PluginsEnum_t pid );
bool AreLoaded() const;
bool AreAnyLoaded() const;
bool AreAnyInitialized() const;
Threading::Mutex& GetMutex() { return m_mtx_PluginStatus; }
virtual void Init();
virtual void Shutdown();
virtual void Open();
@ -307,11 +293,10 @@ public:
virtual void Close( PluginsEnum_t pid );
virtual void Close();
virtual bool IsOpen( PluginsEnum_t pid ) const { return m_info[pid].IsOpened; }
virtual bool NeedsClose() const;
virtual bool NeedsOpen() const;
virtual bool IsOpen( PluginsEnum_t pid ) const;
virtual bool IsInitialized( PluginsEnum_t pid ) const;
virtual bool IsLoaded( PluginsEnum_t pid ) const;
virtual void Freeze( PluginsEnum_t pid, SaveStateBase& state );
virtual bool DoFreeze( PluginsEnum_t pid, int mode, freezeData* data );
@ -320,15 +305,18 @@ public:
virtual void SetSettingsFolder( const wxString& folder );
virtual void SendSettingsFolder();
const wxString& GetName( PluginsEnum_t pid ) const { return m_info[pid].Name; }
const wxString& GetVersion( PluginsEnum_t pid ) const { return m_info[pid].Version; }
friend PluginManager* PluginManager_Create( const wxChar* (&folders)[PluginId_Count] );
const wxString GetName( PluginsEnum_t pid ) const;
const wxString GetVersion( PluginsEnum_t pid ) const;
protected:
void BindCommon( PluginsEnum_t pid );
void BindRequired( PluginsEnum_t pid );
void BindOptional( PluginsEnum_t pid );
virtual bool NeedsClose() const;
virtual bool NeedsOpen() const;
virtual bool NeedsShutdown() const;
virtual bool NeedsInit() const;
virtual bool NeedsLoad() const;
virtual bool NeedsUnload() const;
virtual bool OpenPlugin_GS();
virtual bool OpenPlugin_CDVD();
@ -337,6 +325,8 @@ protected:
virtual bool OpenPlugin_DEV9();
virtual bool OpenPlugin_USB();
virtual bool OpenPlugin_FW();
void _generalclose( PluginsEnum_t pid );
virtual void ClosePlugin_GS();
virtual void ClosePlugin_CDVD();
@ -350,11 +340,13 @@ protected:
};
extern const PluginInfo tbl_PluginInfo[];
extern PluginManager* g_plugins;
extern PluginManager* PluginManager_Create( const wxChar* (&folders)[PluginId_Count] );
// GetPluginManager() is a required external implementation. This function is *NOT*
// provided by the PCSX2 core library. It provides an interface for the linking User
// Interface apps or DLLs to reference their own instance of PluginManager (also allowing
// them to extend the class and override virtual methods).
extern PluginManagerBase& GetPluginManager();
extern PluginManager& GetCorePlugins();
// Hack to expose internal MemoryCard plugin:

View File

@ -82,7 +82,7 @@ void cpuReset()
psxReset();
g_GameStarted = false;
g_SkipBiosHack = EmuConfig.SkipBiosSplash;
g_SkipBiosHack = EmuConfig.UseBOOT2Injection;
ElfCRC = 0;
DiscID = L"";

View File

@ -1,598 +0,0 @@
/* 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 te 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 "zlib/zlib.h"
#include "App.h"
#include "HostGui.h"
#include "AppSaveStates.h"
#include "Utilities/EventSource.inl"
class _BaseStateThread;
template class EventSource<IEventListener_SaveStateThread>;
static EventSource<IEventListener_SaveStateThread> m_evtsrc_SaveState;
// Used to hold the current state backup (fullcopy of PS2 memory and plugin states).
static SafeArray<u8> state_buffer;
static _BaseStateThread* current_state_thread = NULL;
// Simple lock boolean for the state buffer being in use by a thread.
static NonblockingMutex state_buffer_lock;
// This boolean tracks if a savestate is actively saving. When a state is saving we
// typically delay program termination to allow the state time to finish its work.
//static bool state_is_saving = false;
// This boolean is to keep the system from resuming emulation until the current state has completely
// uploaded or downloaded itself. It is only modified from the main thread, and should only be read
// from the main thread.
int sys_resume_lock = 0;
static FnType_OnThreadComplete* Callback_FreezeFinished = NULL;
static bool StateCopy_ForceClear()
{
sys_resume_lock = 0;
state_buffer_lock.Release();
state_buffer.Dispose();
}
class _BaseStateThread : public PersistentThread,
public virtual EventListener_AppStatus,
public virtual IDeletableObject
{
typedef PersistentThread _parent;
protected:
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 restored automatically on completion.
bool m_resume_when_done;
public:
virtual ~_BaseStateThread() throw()
{
if( !m_isStarted ) return;
// Assertion fails because C++ changes the 'this' pointer to the base class since
// derived classes have been deallocated at this point the destructor!
//pxAssumeDev( current_state_thread == this, wxCharNull );
current_state_thread = NULL;
state_buffer_lock.Release(); // just in case;
}
virtual bool IsFreezing() const=0;
protected:
_BaseStateThread( const char* name, FnType_OnThreadComplete* onFinished )
{
Callback_FreezeFinished = onFinished;
m_name = L"StateThread::" + fromUTF8(name);
m_isStarted = false;
m_resume_when_done = false;
}
void OnStart()
{
if( !state_buffer_lock.TryAcquire() )
throw Exception::CancelEvent( m_name + L"request ignored: state copy buffer is already locked!" );
_parent::OnStart();
current_state_thread = this;
m_isStarted = true;
}
void SendFinishEvent( int type )
{
wxGetApp().PostCommand( this, pxEvt_FreezeThreadFinished, type, m_resume_when_done );
}
void AppStatusEvent_OnExit()
{
Cancel();
wxGetApp().DeleteObject( this );
}
};
// --------------------------------------------------------------------------------------
// StateThread_Freeze
// --------------------------------------------------------------------------------------
class StateThread_Freeze : public _BaseStateThread
{
typedef _BaseStateThread _parent;
public:
StateThread_Freeze( FnType_OnThreadComplete* onFinished ) : _BaseStateThread( "Freeze", onFinished )
{
if( !SysHasValidState() )
throw Exception::RuntimeError( L"Cannot complete state freeze request; the virtual machine state is reset.", _("You'll need to start a new virtual machine before you can save its state.") );
}
bool IsFreezing() const { return true; }
protected:
void OnStart()
{
try
{
++sys_resume_lock;
m_resume_when_done = CoreThread.Pause();
_parent::OnStart();
}
catch( ... )
{
wxGetApp().DeleteObject( this );
throw;
}
}
void ExecuteTaskInThread()
{
memSavingState( state_buffer ).FreezeAll();
}
void OnCleanupInThread()
{
SendFinishEvent( SaveStateAction_CreateFinished );
_parent::OnCleanupInThread();
}
};
static const char SavestateIdentString[] = "PCSX2 Savestate";
static const uint SavestateIdentLen = sizeof(SavestateIdentString);
// --------------------------------------------------------------------------------------
// StateThread_ZipToDisk
// --------------------------------------------------------------------------------------
class StateThread_ZipToDisk : public _BaseStateThread
{
typedef _BaseStateThread _parent;
protected:
const wxString m_filename;
gzFile m_gzfp;
public:
StateThread_ZipToDisk( FnType_OnThreadComplete* onFinished, bool resume_done, const wxString& file )
: _BaseStateThread( "ZipToDisk", onFinished )
, m_filename( file )
{
m_gzfp = NULL;
m_resume_when_done = resume_done;
}
virtual ~StateThread_ZipToDisk() throw()
{
if( m_gzfp != NULL ) gzclose( m_gzfp );
}
bool IsFreezing() const { return true; }
protected:
void OnStart()
{
try
{
m_gzfp = gzopen( m_filename.ToUTF8(), "wb" );
if( m_gzfp == NULL )
throw Exception::CreateStream( m_filename, "Cannot create savestate file for writing." );
_parent::OnStart();
}
catch( ... )
{
wxGetApp().DeleteObject( this );
throw;
}
}
void ExecuteTaskInThread()
{
Yield( 3 );
static const int BlockSize = 0x20000;
int curidx = 0;
gzsetparams(m_gzfp, Z_BEST_SPEED, Z_FILTERED); // Best speed at good compression
gzbuffer(m_gzfp, 0x100000); // 1mb buffer size for less file fragments
gzwrite(m_gzfp, SavestateIdentString, sizeof( SavestateIdentString ));
gzwrite(m_gzfp, &g_SaveVersion, sizeof( g_SaveVersion ));
do
{
int thisBlockSize = std::min( BlockSize, state_buffer.GetSizeInBytes() - curidx );
if( gzwrite( m_gzfp, state_buffer.GetPtr(curidx), thisBlockSize ) < thisBlockSize )
throw Exception::BadStream( m_filename );
curidx += thisBlockSize;
Yield( 1 );
} while( curidx < state_buffer.GetSizeInBytes() );
Console.WriteLn( "State saved to disk without error." );
}
void OnCleanupInThread()
{
SendFinishEvent( SaveStateAction_ZipToDiskFinished );
_parent::OnCleanupInThread();
}
};
// --------------------------------------------------------------------------------------
// StateThread_UnzipFromDisk
// --------------------------------------------------------------------------------------
class StateThread_UnzipFromDisk : public _BaseStateThread
{
typedef _BaseStateThread _parent;
protected:
const wxString m_filename;
gzFile m_gzfp;
// set true only once the whole file has finished loading. IF the thread is canceled or
// an error occurs, this will remain false.
bool m_finished;
public:
StateThread_UnzipFromDisk( FnType_OnThreadComplete* onFinished, bool resume_done, const wxString& file )
: _BaseStateThread( "UnzipFromDisk", onFinished )
, m_filename( file )
{
m_gzfp = NULL;
m_finished = false;
m_resume_when_done = resume_done;
}
virtual ~StateThread_UnzipFromDisk() throw()
{
if( m_gzfp != NULL ) gzclose( m_gzfp );
}
bool IsFreezing() const { return false; }
protected:
void OnStart()
{
try
{
m_gzfp = gzopen( m_filename.ToUTF8(), "rb" );
if( m_gzfp == NULL )
throw Exception::CreateStream( m_filename, "Cannot open savestate file for reading." );
char ident[SavestateIdentLen] = {0};
int result = gzread(m_gzfp, ident, SavestateIdentLen);
if( result == -1 )
throw Exception::SaveStateLoadError( m_filename, "Unable to read any data from the gzip archive." );
if( result < SavestateIdentLen )
throw Exception::SaveStateLoadError( m_filename );
if( strcmp(SavestateIdentString, ident) )
throw Exception::SaveStateLoadError( m_filename,
wxsFormat( L"Unrecognized file signature while loading savestate: %s", ident ),
_("File is not a valid PCSX2 savestate, or is from an older unsupported version of PCSX2.")
);
u32 savever;
gzread(m_gzfp, &savever, sizeof(g_SaveVersion));
if( (savever >> 16) != (g_SaveVersion >> 16) )
throw Exception::SaveStateLoadError( m_filename,
wxsFormat( L"Unrecognized file signature while loading savestate: %s", ident ),
_("File is not a valid PCSX2 savestate, or is from an older unsupported version of PCSX2.")
);
if( savever > g_SaveVersion )
throw Exception::SaveStateLoadError( m_filename,
wxsFormat( L"Unrecognized file signature while loading savestate: %s", ident ),
_("File is not a valid PCSX2 savestate, or is from an older unsupported version of PCSX2.")
);
_parent::OnStart();
}
catch( ... )
{
wxGetApp().DeleteObject( this );
throw;
}
}
void ExecuteTaskInThread()
{
// fixme: should start initially with the file size, and then grow from there.
static const int BlockSize = 0x100000;
state_buffer.MakeRoomFor( 0x800000 ); // start with an 8 meg buffer to avoid frequent reallocation.
gzbuffer(m_gzfp, 0x100000); // 1mb buffer for zlib internal operations
int curidx = 0;
do
{
state_buffer.MakeRoomFor( curidx+BlockSize );
gzread( m_gzfp, state_buffer.GetPtr(curidx), BlockSize );
curidx += BlockSize;
TestCancel();
} while( !gzeof(m_gzfp) );
m_finished = true;
}
void OnCleanupInThread()
{
SendFinishEvent( SaveStateAction_UnzipFromDiskFinished );
_parent::OnCleanupInThread();
}
};
void Pcsx2App::OnFreezeThreadFinished( wxCommandEvent& evt )
{
// clear the OnFreezeFinished to NULL now, in case of error.
// (but only actually run it if no errors occur)
FnType_OnThreadComplete* fn_tmp = Callback_FreezeFinished;
Callback_FreezeFinished = NULL;
{
ScopedPtr<PersistentThread> thr( (PersistentThread*)evt.GetClientData() );
if( !pxAssertDev( thr != NULL, "NULL thread handle on freeze finished?" ) ) return;
current_state_thread = NULL;
state_buffer_lock.Release();
--sys_resume_lock;
m_evtsrc_SaveState.Dispatch( (SaveStateActionType)evt.GetInt() );
thr->RethrowException();
}
if( fn_tmp != NULL ) fn_tmp( evt );
}
static void OnFinished_Resume( const wxCommandEvent& evt )
{
CoreThread.RecoverState();
if( evt.GetExtraLong() ) CoreThread.Resume();
}
static wxString zip_dest_filename;
static void OnFinished_ZipToDisk( const wxCommandEvent& evt )
{
if( !pxAssertDev( evt.GetInt() == SaveStateAction_CreateFinished, "Unexpected StateThreadAction value, aborting save." ) ) return;
if( zip_dest_filename.IsEmpty() )
{
Console.Warning( "Cannot save state to disk: empty filename specified." );
return;
}
// Phase 2: Record to disk!!
(new StateThread_ZipToDisk( NULL, !!evt.GetExtraLong(), zip_dest_filename ))->Start();
CoreThread.Resume();
}
class InvokeAction_WhenSaveComplete :
public IEventListener_SaveStateThread,
public IDeletableObject
{
protected:
IActionInvocation* m_action;
public:
InvokeAction_WhenSaveComplete( IActionInvocation* action )
{
m_action = action;
}
virtual ~InvokeAction_WhenSaveComplete() throw() {}
void SaveStateAction_OnZipToDiskFinished()
{
if( m_action )
{
m_action->InvokeAction();
safe_delete( m_action );
}
wxGetApp().DeleteObject( this );
}
};
class InvokeAction_WhenStateCopyComplete : public InvokeAction_WhenSaveComplete
{
public:
InvokeAction_WhenStateCopyComplete( IActionInvocation* action )
: InvokeAction_WhenSaveComplete( action )
{
}
virtual ~InvokeAction_WhenStateCopyComplete() throw() {}
void SaveStateAction_OnCreateFinished()
{
SaveStateAction_OnZipToDiskFinished();
}
};
// =====================================================================================================
// StateCopy Public Interface
// =====================================================================================================
bool StateCopy_InvokeOnSaveComplete( IActionInvocation* sst )
{
AffinityAssert_AllowFromMain();
if( current_state_thread == NULL || !current_state_thread->IsFreezing() )
{
delete sst;
return false;
}
m_evtsrc_SaveState.Add( new InvokeAction_WhenSaveComplete( sst ) );
return true;
}
bool StateCopy_InvokeOnCopyComplete( IActionInvocation* sst )
{
AffinityAssert_AllowFromMain();
if( current_state_thread == NULL || !current_state_thread->IsFreezing() )
{
delete sst;
return false;
}
m_evtsrc_SaveState.Add( new InvokeAction_WhenStateCopyComplete( sst ) );
return true;
}
void StateCopy_SaveToFile( const wxString& file )
{
if( state_buffer_lock.IsLocked() ) return;
zip_dest_filename = file;
(new StateThread_Freeze( OnFinished_ZipToDisk ))->Start();
Console.WriteLn( Color_StrongGreen, L"Saving savestate to file: %s", zip_dest_filename.c_str() );
}
void StateCopy_LoadFromFile( const wxString& file )
{
if( state_buffer_lock.IsLocked() ) return;
bool resume_when_done = CoreThread.Pause();
(new StateThread_UnzipFromDisk( OnFinished_Resume, resume_when_done, file ))->Start();
}
// Saves recovery state info to the given saveslot, or saves the active emulation state
// (if one exists) and no recovery data was found. This is needed because when a recovery
// state is made, the emulation state is usually reset so the only persisting state is
// the one in the memory save. :)
void StateCopy_SaveToSlot( uint num )
{
if( state_buffer_lock.IsLocked() ) return;
zip_dest_filename = SaveStateBase::GetFilename( num );
(new StateThread_Freeze( OnFinished_ZipToDisk ))->Start();
Console.WriteLn( Color_StrongGreen, "Saving savestate to slot %d...", num );
Console.Indent().WriteLn( Color_StrongGreen, L"filename: %s", zip_dest_filename.c_str() );
}
void StateCopy_LoadFromSlot( uint slot )
{
if( state_buffer_lock.IsLocked() ) return;
wxString file( SaveStateBase::GetFilename( slot ) );
if( !wxFileExists( file ) )
{
Console.Warning( "Savestate slot %d is empty.", slot );
return;
}
Console.WriteLn( Color_StrongGreen, "Loading savestate from slot %d...", slot );
Console.Indent().WriteLn( Color_StrongGreen, L"filename: %s", file.c_str() );
bool resume_when_done = CoreThread.Pause();
(new StateThread_UnzipFromDisk( OnFinished_Resume, resume_when_done, file ))->Start();
}
bool StateCopy_IsValid()
{
return !state_buffer.IsDisposed();
}
const SafeArray<u8>* StateCopy_GetBuffer()
{
if( state_buffer_lock.IsLocked() || state_buffer.IsDisposed() ) return NULL;
return &state_buffer;
}
void StateCopy_FreezeToMem()
{
if( state_buffer_lock.IsLocked() ) return;
(new StateThread_Freeze( OnFinished_Resume ))->Start();
}
class Acquire_And_Block
{
protected:
bool m_DisposeWhenFinished;
bool m_Acquired;
public:
Acquire_And_Block( bool dispose )
{
m_DisposeWhenFinished = dispose;
m_Acquired = false;
/*
// If the state buffer is locked and we're being called from the main thread then we need
// to cancel the current action. This is needed because state_buffer_lock is only updated
// from events handled on the main thread.
if( wxThread::IsMain() )
throw Exception::CancelEvent( "Blocking ThawFromMem canceled due to existing state buffer lock." );
else*/
while ( !state_buffer_lock.TryAcquire() )
{
pxAssume( current_state_thread != NULL );
current_state_thread->Block();
wxGetApp().ProcessPendingEvents(); // Trying this for now, may or may not work due to recursive pitfalls (see above)
};
m_Acquired = true;
}
virtual ~Acquire_And_Block() throw()
{
if( m_DisposeWhenFinished )
state_buffer.Dispose();
if( m_Acquired )
state_buffer_lock.Release();
}
};
void StateCopy_FreezeToMem_Blocking()
{
Acquire_And_Block blocker( false );
memSavingState( state_buffer ).FreezeAll();
}
// Copies the saved state into the active VM, and automatically free's the saved state data.
void StateCopy_ThawFromMem_Blocking()
{
Acquire_And_Block blocker( true );
memLoadingState( state_buffer ).FreezeAll();
}
void StateCopy_Clear()
{
if( state_buffer_lock.IsLocked() ) return;
state_buffer.Dispose();
}
bool StateCopy_IsBusy()
{
return state_buffer_lock.IsLocked();
}

View File

@ -47,8 +47,18 @@ wxString SaveStateBase::GetFilename( int slot )
}
SaveStateBase::SaveStateBase( SafeArray<u8>& memblock )
: m_memory( memblock )
{
Init( &memblock );
}
SaveStateBase::SaveStateBase( SafeArray<u8>* memblock )
{
Init( memblock );
}
void SaveStateBase::Init( SafeArray<u8>* memblock )
{
m_memory = memblock;
m_version = g_SaveVersion;
m_idx = 0;
m_sectid = FreezeId_Unknown;
@ -58,12 +68,14 @@ SaveStateBase::SaveStateBase( SafeArray<u8>& memblock )
void SaveStateBase::PrepBlock( int size )
{
pxAssumeDev( m_memory, "Savestate memory/buffer pointer is null!" );
const int end = m_idx+size;
if( IsSaving() )
m_memory.MakeRoomFor( end );
m_memory->MakeRoomFor( end );
else
{
if( m_memory.GetSizeInBytes() < end )
if( m_memory->GetSizeInBytes() < end )
throw Exception::SaveStateLoadError();
}
}
@ -203,7 +215,7 @@ void SaveStateBase::WritebackSectionLength( int seekpos, int sectlen, const wxCh
if( IsSaving() )
{
// write back the section length...
*((u32*)m_memory.GetPtr(seekpos-4)) = realsectsize;
*((u32*)m_memory->GetPtr(seekpos-4)) = realsectsize;
}
else // IsLoading!!
{
@ -304,7 +316,7 @@ bool SaveStateBase::FreezeSection( int seek_section )
if( isSeeking )
m_idx += sectlen;
else
g_plugins->Freeze( (PluginsEnum_t)m_pid, *this );
GetCorePlugins().Freeze( (PluginsEnum_t)m_pid, *this );
WritebackSectionLength( seekpos, sectlen, L"Plugins" );
@ -364,19 +376,26 @@ memSavingState::memSavingState( SafeArray<u8>& save_to )
{
}
memSavingState::memSavingState( SafeArray<u8>* save_to )
: SaveStateBase( save_to )
{
}
// Saving of state data
void memSavingState::FreezeMem( void* data, int size )
{
m_memory.MakeRoomFor( m_idx+size );
memcpy_fast( m_memory.GetPtr(m_idx), data, size );
m_memory->MakeRoomFor( m_idx+size );
memcpy_fast( m_memory->GetPtr(m_idx), data, size );
m_idx += size;
}
void memSavingState::FreezeAll()
{
pxAssumeDev( m_memory, "Savestate memory/buffer pointer is null!" );
// 90% of all savestates fit in under 45 megs (and require more than 43 megs, so might as well...)
m_memory.ChunkSize = ReallocThreshold;
m_memory.MakeRoomFor( MemoryBaseAllocSize );
m_memory->ChunkSize = ReallocThreshold;
m_memory->MakeRoomFor( MemoryBaseAllocSize );
_parent::FreezeAll();
}
@ -386,12 +405,17 @@ memLoadingState::memLoadingState( const SafeArray<u8>& load_from )
{
}
memLoadingState::memLoadingState( const SafeArray<u8>* load_from )
: SaveStateBase( const_cast<SafeArray<u8>*>(load_from) )
{
}
memLoadingState::~memLoadingState() throw() { }
// Loading of state data
void memLoadingState::FreezeMem( void* data, int size )
{
const u8* const src = m_memory.GetPtr(m_idx);
const u8* const src = m_memory->GetPtr(m_idx);
m_idx += size;
memcpy_fast( data, src, size );
}

View File

@ -110,7 +110,7 @@ namespace Exception
class SaveStateBase
{
protected:
SafeArray<u8>& m_memory;
SafeArray<u8>* m_memory;
char m_tagspace[32];
u32 m_version; // version of the savestate being loaded.
@ -123,6 +123,7 @@ protected:
public:
SaveStateBase( SafeArray<u8>& memblock );
SaveStateBase( SafeArray<u8>* memblock );
virtual ~SaveStateBase() { }
static wxString GetFilename( int slot );
@ -159,7 +160,7 @@ public:
u8* GetBlockPtr()
{
return &m_memory[m_idx];
return m_memory->GetPtr(m_idx);
}
void CommitBlock( int size )
@ -190,6 +191,7 @@ public:
void gsFreeze();
protected:
void Init( SafeArray<u8>* memblock );
// Load/Save functions for the various components of our glorious emulator!
@ -233,6 +235,7 @@ protected:
public:
virtual ~memSavingState() throw() { }
memSavingState( SafeArray<u8>& save_to );
memSavingState( SafeArray<u8>* save_to );
// Saving of state data to a memory buffer
void FreezeMem( void* data, int size );
@ -245,13 +248,15 @@ class memLoadingState : public SaveStateBase
{
public:
virtual ~memLoadingState() throw();
memLoadingState( const SafeArray<u8>& load_from );
memLoadingState( const SafeArray<u8>* load_from );
// Loading of state data from a memory buffer...
void FreezeMem( void* data, int size );
bool SeekToSection( PluginsEnum_t pid );
bool IsSaving() const { return false; }
bool IsFinished() const { return m_idx >= m_memory.GetSizeInBytes(); }
bool IsFinished() const { return m_idx >= m_memory->GetSizeInBytes(); }
};

View File

@ -64,21 +64,21 @@ const Pcsx2Config EmuConfig;
Pcsx2Config::GSOptions& SetGSConfig()
{
//DbgCon.WriteLn( "Direct modification of EmuConfig.GS detected" );
AffinityAssert_AllowFromMain();
AffinityAssert_AllowFrom_MainUI();
return const_cast<Pcsx2Config::GSOptions&>(EmuConfig.GS);
}
ConsoleLogFilters& SetConsoleConfig()
{
//DbgCon.WriteLn( "Direct modification of EmuConfig.Log detected" );
AffinityAssert_AllowFromMain();
AffinityAssert_AllowFrom_MainUI();
return const_cast<ConsoleLogFilters&>(EmuConfig.Log);
}
TraceLogFilters& SetTraceConfig()
{
//DbgCon.WriteLn( "Direct modification of EmuConfig.TraceLog detected" );
AffinityAssert_AllowFromMain();
AffinityAssert_AllowFrom_MainUI();
return const_cast<TraceLogFilters&>(EmuConfig.Trace);
}

View File

@ -57,25 +57,27 @@ void SysCoreThread::Cancel( bool isBlocking )
{
m_CoreCancelDamnit = true;
_parent::Cancel();
ReleaseResumeLock();
}
bool SysCoreThread::Cancel( const wxTimeSpan& span )
{
m_CoreCancelDamnit = true;
if( _parent::Cancel( span ) )
{
ReleaseResumeLock();
return true;
}
return false;
}
void SysCoreThread::OnStart()
{
m_CoreCancelDamnit = false;
_parent::OnStart();
}
void SysCoreThread::Start()
{
if( g_plugins == NULL ) return;
g_plugins->Init();
m_CoreCancelDamnit = false; // belongs in OnStart actually, but I'm tired :P
if( !GetCorePlugins().AreLoaded() ) return;
GetCorePlugins().Init();
_parent::Start();
}
@ -101,8 +103,8 @@ void SysCoreThread::OnResumeReady()
// resumed manually).
void SysCoreThread::RecoverState()
{
Pause();
m_resetVirtualMachine = true;
pxAssumeDev( IsPaused(), "Unsafe use of RecoverState function; Corethread is not paused/closed." );
m_resetRecompilers = true;
m_hasValidState = false;
}
@ -122,50 +124,6 @@ void SysCoreThread::SetElfOverride( const wxString& elf )
m_elf_override = elf;
}
ScopedCoreThreadSuspend::ScopedCoreThreadSuspend()
{
m_ResumeWhenDone = GetCoreThread().Suspend();
}
ScopedCoreThreadSuspend::~ScopedCoreThreadSuspend() throw()
{
if( m_ResumeWhenDone )
{
Console.WriteLn( Color_Gray, "Scoped CoreThread suspend was not allowed to resume." );
}
}
// Resumes CoreThread execution, but *only* if it was in a running state when this object
// was instanized. Subsequent calls to Resume() will be ignored.
void ScopedCoreThreadSuspend::Resume()
{
if( m_ResumeWhenDone )
GetCoreThread().Resume();
m_ResumeWhenDone = false;
}
ScopedCoreThreadPause::ScopedCoreThreadPause()
{
m_ResumeWhenDone = GetCoreThread().Pause();
}
ScopedCoreThreadPause::~ScopedCoreThreadPause() throw()
{
if( m_ResumeWhenDone )
{
Console.WriteLn( Color_Gray, "Scoped CoreThread pause was not allowed to resume." );
}
}
// Resumes CoreThread execution, but *only* if it was in a running state when this object
// was instanized. Subsequent calls to Resume() will be ignored.
void ScopedCoreThreadPause::Resume()
{
if( m_ResumeWhenDone )
GetCoreThread().Resume();
m_ResumeWhenDone = false;
}
// Applies a full suite of new settings, which will automatically facilitate the necessary
// resets of the core and components (including plugins, if needed). The scope of resetting
@ -175,27 +133,14 @@ void SysCoreThread::ApplySettings( const Pcsx2Config& src )
{
if( src == EmuConfig ) return;
ScopedCoreThreadPause sys_paused;
if( !pxAssertDev( IsPaused(), "CoreThread is not paused; settings cannot be applied." ) ) return;
m_resetRecompilers = ( src.Cpu != EmuConfig.Cpu ) || ( src.Recompiler != EmuConfig.Recompiler ) ||
( src.Gamefixes != EmuConfig.Gamefixes ) || ( src.Speedhacks != EmuConfig.Speedhacks );
m_resetProfilers = ( src.Profiler != EmuConfig.Profiler );
m_resetVsyncTimers = ( src.GS != EmuConfig.GS );
const_cast<Pcsx2Config&>(EmuConfig) = src;
sys_paused.Resume();
}
void SysCoreThread::ChangeCdvdSource( CDVD_SourceType type )
{
if( type == CDVDsys_GetSourceType() ) return;
// Fast change of the CDVD source only -- a Pause will suffice.
bool resumeWhenDone = Pause();
GetPluginManager().Close( PluginId_CDVD );
CDVDsys_ChangeSource( type );
if( resumeWhenDone ) Resume();
}
// --------------------------------------------------------------------------------------
@ -306,6 +251,13 @@ void SysCoreThread::StateCheckInThread()
_reset_stuff_as_needed(); // kinda redundant but could catch unexpected threaded state changes...
}
// Allows an override point and solves an SEH "exception-type boundary" problem (can't mix
// SEH and C++ exceptions in the same function).
void SysCoreThread::DoCpuExecute()
{
Cpu->Execute();
}
void SysCoreThread::ExecuteTaskInThread()
{
Threading::EnableHiresScheduler();
@ -317,21 +269,19 @@ void SysCoreThread::ExecuteTaskInThread()
PCSX2_PAGEFAULT_PROTECT {
do {
StateCheckInThread();
Cpu->Execute();
DoCpuExecute();
} while( true );
} PCSX2_PAGEFAULT_EXCEPT;
}
void SysCoreThread::OnSuspendInThread()
{
if( g_plugins != NULL )
g_plugins->Close();
GetCorePlugins().Close();
}
void SysCoreThread::OnResumeInThread( bool isSuspended )
{
if( g_plugins != NULL )
g_plugins->Open();
GetCorePlugins().Open();
CpuInitializeMess();
}
@ -346,8 +296,7 @@ void SysCoreThread::OnCleanupInThread()
Threading::DisableHiresScheduler();
if( g_plugins != NULL )
g_plugins->Close();
GetCorePlugins().Close();
tls_coreThread = NULL;
_parent::OnCleanupInThread();

View File

@ -51,34 +51,15 @@ void SysThreadBase::OnStart()
{
if( !pxAssertDev( m_ExecMode == ExecMode_NoThreadYet, "SysSustainableThread:Start(): Invalid execution mode" ) ) return;
m_ResumeEvent.Reset();
m_sem_Resume.Reset();
m_sem_ChangingExecMode.Reset();
FrankenMutex( m_ExecModeMutex );
FrankenMutex( m_RunningLock );
_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.
//
@ -95,21 +76,19 @@ void SysThreadBase::ThrowDeadlockException()
// Exceptions:
// CancelEvent - thrown if the thread is already in a Paused or Closing state. Because
// actions that pause emulation typically rely on plugins remaining loaded/active,
// Suspension must cansel itself forcefully or risk crashing whatever other action is
// Suspension must cancel 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 )
void SysThreadBase::Suspend( bool isBlocking )
{
if( IsSelf() || !IsRunning() ) return false;
if( IsSelf() || !IsRunning() ) return;
// shortcut ExecMode check to avoid deadlocking on redundant calls to Suspend issued
// from Resume or OnResumeReady code.
if( m_ExecMode == ExecMode_Closed ) return false;
bool retval = false;
if( m_ExecMode == ExecMode_Closed ) return;
{
ScopedLock locker( m_ExecModeMutex );
@ -117,16 +96,20 @@ bool SysThreadBase::Suspend( bool isBlocking )
switch( m_ExecMode )
{
// Check again -- status could have changed since above.
case ExecMode_Closed: return false;
case ExecMode_Closed: return;
case ExecMode_Pausing:
case ExecMode_Paused:
if( !isBlocking ) return retval;
throw Exception::CancelEvent( "Another thread is pausing the VM state." );
if( !isBlocking )
throw Exception::CancelEvent( "Cannot suspend in non-blocking fashion: Another thread is pausing the VM state." );
m_ExecMode = ExecMode_Closing;
m_sem_Resume.Post();
m_sem_ChangingExecMode.Wait();
break;
case ExecMode_Opened:
m_ExecMode = ExecMode_Closing;
retval = true;
break;
}
@ -135,40 +118,29 @@ bool SysThreadBase::Suspend( bool isBlocking )
}
if( isBlocking )
{
if( !m_RunningLock.Wait( GetDeadlockTimeout() ) )
{
DoThreadDeadlocked();
}
}
return retval;
m_RunningLock.Wait();
}
// Returns:
// The previous suspension state; true if the thread was running or false if it was
// closed, not running, or paused.
//
bool SysThreadBase::Pause()
void SysThreadBase::Pause()
{
if( IsSelf() || !IsRunning() ) return false;
if( IsSelf() || !IsRunning() ) return;
// shortcut ExecMode check to avoid deadlocking on redundant calls to Suspend issued
// from Resume or OnResumeReady code.
if( (m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused) ) return false;
bool retval = false;
if( (m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused) ) return;
{
ScopedLock locker( m_ExecModeMutex );
// Check again -- status could have changed since above.
if( (m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused) ) return false;
if( (m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused) ) return;
if( m_ExecMode == ExecMode_Opened )
{
m_ExecMode = ExecMode_Pausing;
retval = true;
}
pxAssumeDev( m_ExecMode == ExecMode_Pausing, "ExecMode should be nothing other than Pausing..." );
@ -176,8 +148,6 @@ bool SysThreadBase::Pause()
}
m_RunningLock.Wait();
return retval;
}
// Resumes the core execution state, or does nothing is the core is already running. If
@ -199,9 +169,6 @@ void SysThreadBase::Resume()
if( IsSelf() ) return;
if( m_ExecMode == ExecMode_Opened ) return;
ScopedNonblockingLock resprotect( m_ResumeProtection );
if( resprotect.Failed() ) return;
ScopedLock locker( m_ExecModeMutex );
// Implementation Note:
@ -231,7 +198,7 @@ void SysThreadBase::Resume()
m_RunningLock.Wait();
if( !m_running ) return;
if( (m_ExecMode != ExecMode_Closed) && (m_ExecMode != ExecMode_Paused) ) return;
if( g_plugins == NULL ) return;
if( !GetCorePlugins().AreLoaded() ) return;
break;
}
@ -240,7 +207,7 @@ void SysThreadBase::Resume()
OnResumeReady();
m_ExecMode = ExecMode_Opened;
m_ResumeEvent.Post();
m_sem_Resume.Post();
}
@ -296,11 +263,17 @@ void SysThreadBase::StateCheckInThread()
case ExecMode_Paused:
while( m_ExecMode == ExecMode_Paused )
m_ResumeEvent.WaitWithoutYield();
m_sem_Resume.WaitWithoutYield();
m_RunningLock.Acquire();
OnResumeInThread( false );
break;
if( m_ExecMode != ExecMode_Closing )
{
OnResumeInThread( false );
break;
}
m_sem_ChangingExecMode.Post();
// fallthrough if we're switching to closing state...
// -------------------------------------
case ExecMode_Closing:
@ -313,7 +286,7 @@ void SysThreadBase::StateCheckInThread()
case ExecMode_Closed:
while( m_ExecMode == ExecMode_Closed )
m_ResumeEvent.WaitWithoutYield();
m_sem_Resume.WaitWithoutYield();
m_RunningLock.Acquire();
OnResumeInThread( true );

View File

@ -16,60 +16,48 @@
#pragma once
#include "Utilities/PersistentThread.h"
#include "Utilities/RwMutex.h"
#include "x86emitter/tools.h"
#include "CDVD/CDVDaccess.h"
using namespace Threading;
// --------------------------------------------------------------------------------------
// ISysThread
// --------------------------------------------------------------------------------------
class ISysThread : public virtual IThread
{
public:
ISysThread() {}
virtual ~ISysThread() throw() {}
virtual bool Suspend( bool isBlocking = true ) { return false; }
virtual bool Pause() { return false; }
virtual void Resume() {}
};
// --------------------------------------------------------------------------------------
// SysThreadBase
// --------------------------------------------------------------------------------------
class SysThreadBase : public PersistentThread, public virtual ISysThread
class SysThreadBase : public PersistentThread
{
typedef PersistentThread _parent;
public:
// Important: The order of these enumerations matters. All "not-open" statuses must
// be listed before ExecMode_Closed, since there are "optimized" tests that rely on the
// assumption that "ExecMode <= ExecMode_Closed" equates to a closed thread status.
// Important: The order of these enumerations matters! Optimized tests are used for both
// Closed and Paused states.
enum ExecutionMode
{
// Thread has not been created yet. Typically this is the same as IsRunning()
// returning FALSE.
ExecMode_NoThreadYet,
// Close signal has been sent to the thread, but the thread's response is still
// pending (thread is busy/running).
ExecMode_Closing,
// Thread is safely paused, with plugins in a "closed" state, and waiting for a
// resume/open signal.
ExecMode_Closed,
// Thread is safely paused, with plugins in an "open" state, and waiting for a
// resume/open signal.
ExecMode_Paused,
// Thread is active and running, with pluigns in an "open" state.
ExecMode_Opened,
// Close signal has been sent to the thread, but the thread's response is still
// pending (thread is busy/running).
ExecMode_Closing,
// Pause signal has been sent to the thread, but the thread's response is still
// pending (thread is busy/running).
ExecMode_Pausing,
// Thread is safely paused, with plugins in an "open" state, and waiting for a
// resume/open signal.
ExecMode_Paused,
};
protected:
@ -77,20 +65,19 @@ protected:
// This lock is used to avoid simultaneous requests to Suspend/Resume/Pause from
// contending threads.
MutexLockRecursive m_ExecModeMutex;
MutexRecursive m_ExecModeMutex;
// Used to wake up the thread from sleeping when it's in a suspended state.
Semaphore m_ResumeEvent;
Semaphore m_sem_Resume;
// Used to synchronize inline changes from paused to suspended status.
Semaphore m_sem_ChangingExecMode;
// Locked whenever the thread is not in a suspended state (either closed or paused).
// Issue a Wait against this mutex for performing actions that require the thread
// to be suspended.
Mutex m_RunningLock;
// Protects the thread from re-entrant resume requests while dependent resources are
// being constructed.
NonblockingMutex m_ResumeProtection;
Mutex m_RunningLock;
public:
explicit SysThreadBase();
virtual ~SysThreadBase() throw();
@ -102,32 +89,27 @@ public:
// first.
bool IsOpen() const
{
return m_ExecMode > ExecMode_Closed;
return IsRunning() && (m_ExecMode > ExecMode_Closed);
}
bool IsClosed() const { return !IsOpen(); }
bool IsPaused() const { return !IsRunning() || (m_ExecMode <= ExecMode_Paused); }
bool HasPendingStateChangeRequest() const
{
ExecutionMode mode = m_ExecMode;
return (mode == ExecMode_Closing) || (mode == ExecMode_Pausing);
}
bool IsClosed() const { return !IsOpen(); }
ExecutionMode GetExecutionMode() const { return m_ExecMode; }
Mutex& ExecutionModeMutex() { return m_ExecModeMutex; }
virtual bool Suspend( bool isBlocking = true );
virtual void Suspend( bool isBlocking = true );
virtual void Resume();
virtual bool Pause();
virtual bool AcquireResumeLock() { return m_ResumeProtection.TryAcquire(); }
virtual void ReleaseResumeLock() { m_ResumeProtection.Release(); }
virtual wxTimeSpan GetDeadlockTimeout() const;
virtual void ThrowDeadlockException();
virtual void Pause();
protected:
virtual void DoThreadDeadlocked();
virtual void OnStart();
// This function is called by Resume immediately prior to releasing the suspension of
@ -146,14 +128,14 @@ protected:
// handles invocation by the following guidelines: Called *in thread* from StateCheckInThread()
// prior to suspending the thread (ie, when Suspend() has been called on a separate
// thread, requesting this thread suspend itself temporarily). After this is called,
// the thread enters a waiting state on the m_ResumeEvent semaphore.
// the thread enters a waiting state on the m_sem_Resume semaphore.
virtual void OnSuspendInThread()=0;
// Extending classes should implement this, but should not call it. The parent class
// handles invocation by the following guidelines: Called *in thread* from StateCheckInThread()
// prior to pausing the thread (ie, when Pause() has been called on a separate thread,
// requesting this thread pause itself temporarily). After this is called, the thread
// enters a waiting state on the m_ResumeEvent semaphore.
// enters a waiting state on the m_sem_Resume semaphore.
virtual void OnPauseInThread()=0;
// Extending classes should implement this, but should not call it. The parent class
@ -165,6 +147,7 @@ protected:
virtual void OnResumeInThread( bool isSuspended )=0;
};
// --------------------------------------------------------------------------------------
// SysCoreThread class
// --------------------------------------------------------------------------------------
@ -173,6 +156,8 @@ class SysCoreThread : public SysThreadBase
typedef SysThreadBase _parent;
protected:
s32 m_CloseTemporary;
bool m_resetRecompilers;
bool m_resetProfilers;
bool m_resetVsyncTimers;
@ -183,7 +168,7 @@ protected:
bool m_CoreCancelDamnit;
wxString m_elf_override;
SSE_MXCSR m_mxcsr_saved;
public:
@ -199,7 +184,7 @@ public:
virtual void RecoverState();
virtual void Cancel( bool isBlocking=true );
virtual bool Cancel( const wxTimeSpan& timeout );
bool HasValidState()
{
return m_hasValidState;
@ -209,55 +194,52 @@ public:
virtual void StateCheckInThread();
virtual void VsyncInThread();
virtual void PostVsyncToUI()=0;
virtual const wxString& GetElfOverride() const { return m_elf_override; }
virtual void SetElfOverride( const wxString& elf );
virtual void ChangeCdvdSource( CDVD_SourceType type );
protected:
void _reset_stuff_as_needed();
virtual void CpuInitializeMess();
virtual void Start();
virtual void OnStart();
virtual void OnSuspendInThread();
virtual void OnPauseInThread() {}
virtual void OnResumeInThread( bool IsSuspended );
virtual void OnCleanupInThread();
virtual void ExecuteTaskInThread();
virtual void DoCpuReset();
virtual void DoCpuExecute();
void _StateCheckThrows();
};
// --------------------------------------------------------------------------------------
// ScopedCoreThreadSuspend
// --------------------------------------------------------------------------------------
// This class behaves a bit differently from other scoped classes due to the "standard"
// assumption that we actually do *not* want to resume CoreThread operations when an
// exception occurs. Because of this, the destructor of this class does *not* unroll the
// suspend operation. Instead you must manually instruct the class to resume using a call
// to the provisioned Resume() method.
//
// If the class leaves scope without having been resumed, a log is written to the console.
// This can be useful for troubleshooting, and also allows the log a second line of info
// indicating the status of CoreThread execution at the time of the exception.
//
struct ScopedCoreThreadSuspend
{
bool m_ResumeWhenDone;
ScopedCoreThreadSuspend();
virtual ~ScopedCoreThreadSuspend() throw();
virtual void Resume();
struct SysStateUnlockedParams
{
SysStateUnlockedParams() {}
};
struct ScopedCoreThreadPause
// --------------------------------------------------------------------------------------
// IEventListener_SaveStateThread
// --------------------------------------------------------------------------------------
class IEventListener_SysState : public IEventDispatcher<SysStateUnlockedParams>
{
bool m_ResumeWhenDone;
public:
typedef SysStateUnlockedParams EvtParams;
ScopedCoreThreadPause();
virtual ~ScopedCoreThreadPause() throw();
virtual void Resume();
public:
IEventListener_SysState() {}
virtual ~IEventListener_SysState() throw() {}
virtual void DispatchEvent( const SysStateUnlockedParams& status )
{
SysStateAction_OnUnlocked();
}
protected:
virtual void SysStateAction_OnUnlocked();
};
// GetCoreThread() is a required external implementation. This function is *NOT*
@ -266,5 +248,3 @@ struct ScopedCoreThreadPause
// them to extend the class and override virtual methods).
//
extern SysCoreThread& GetCoreThread();
extern int sys_resume_lock;

View File

@ -0,0 +1,10 @@
-------------------
ZipTools (folder)
-------------------
Contains C++ interfaces for zipping to/from various formats
(primarily gzip and 7zip).
Notice: This folder is intended to be moved to a utility folder
outside the main PCSX2 folders at a later date.

View File

@ -0,0 +1,103 @@
/* 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/PersistentThread.h"
//#include "zlib/zlib.h"
using namespace Threading;
class IStreamWriter
{
public:
virtual ~IStreamWriter() throw() {}
virtual void Write( const void* data, size_t size )=0;
virtual wxString GetStreamName() const=0;
template< typename T >
void Write( const T& data )
{
Write( &data, sizeof(data) );
}
};
class IStreamReader
{
public:
virtual ~IStreamReader() throw() {}
virtual void Read( void* dest, size_t size )=0;
virtual wxString GetStreamName() const=0;
template< typename T >
void Read( T& dest )
{
Read( &dest, sizeof(dest) );
}
};
typedef void FnType_WriteCompressedHeader( IStreamWriter& thr );
typedef void FnType_ReadCompressedHeader( IStreamReader& thr );
// --------------------------------------------------------------------------------------
// BaseCompressThread
// --------------------------------------------------------------------------------------
class BaseCompressThread
: public PersistentThread
, public IStreamWriter
{
typedef PersistentThread _parent;
protected:
FnType_WriteCompressedHeader* m_WriteHeaderInThread;
const wxString m_filename;
ScopedPtr< SafeArray< u8 > > m_src_buffer;
BaseCompressThread( const wxString& file, SafeArray<u8>* srcdata, FnType_WriteCompressedHeader* writeHeader=NULL)
: m_filename( file )
, m_src_buffer( srcdata )
{
m_WriteHeaderInThread = writeHeader;
}
virtual ~BaseCompressThread() throw() {}
public:
wxString GetStreamName() const { return m_filename; }
};
// --------------------------------------------------------------------------------------
// CompressThread_gzip
// --------------------------------------------------------------------------------------
class CompressThread_gzip : public BaseCompressThread
{
typedef BaseCompressThread _parent;
protected:
gzFile m_gzfp;
public:
CompressThread_gzip( const wxString& file, SafeArray<u8>* srcdata, FnType_WriteCompressedHeader* writeHeader=NULL );
CompressThread_gzip( const wxString& file, ScopedPtr<SafeArray<u8>>& srcdata, FnType_WriteCompressedHeader* writeHeader=NULL );
virtual ~CompressThread_gzip() throw();
protected:
void Write( const void* data, size_t size );
void ExecuteTaskInThread();
void OnCleanupInThread();
};

View File

@ -0,0 +1,79 @@
/* 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 te 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 "SaveState.h"
#include "ThreadedZipTools.h"
CompressThread_gzip::CompressThread_gzip( const wxString& file, SafeArray<u8>* srcdata, FnType_WriteCompressedHeader* writeheader )
: BaseCompressThread( file, srcdata, writeheader )
{
m_gzfp = NULL;
}
CompressThread_gzip::CompressThread_gzip( const wxString& file, ScopedPtr<SafeArray<u8>>& srcdata, FnType_WriteCompressedHeader* writeheader )
: BaseCompressThread( file, srcdata.DetachPtr(), writeheader )
{
m_gzfp = NULL;
}
CompressThread_gzip::~CompressThread_gzip() throw()
{
if( m_gzfp ) gzclose( m_gzfp );
}
void CompressThread_gzip::Write( const void* data, size_t size )
{
if( gzwrite( m_gzfp, data, size ) == 0 )
throw Exception::BadStream( m_filename, "Write to zip file failed." );
}
void CompressThread_gzip::ExecuteTaskInThread()
{
if( !m_src_buffer ) return;
Yield( 3 );
if( !(m_gzfp = gzopen(m_filename.ToUTF8(), "wb")) )
throw Exception::CannotCreateStream( m_filename );
gzsetparams(m_gzfp, Z_BEST_SPEED, Z_FILTERED); // Best speed at good compression
gzbuffer(m_gzfp, 0x100000); // 1mb buffer size for less file fragments (Windows/NTFS)
if( m_WriteHeaderInThread )
m_WriteHeaderInThread( *this );
static const int BlockSize = 0x64000;
int curidx = 0;
do {
int thisBlockSize = std::min( BlockSize, m_src_buffer->GetSizeInBytes() - curidx );
if( gzwrite( m_gzfp, m_src_buffer->GetPtr(curidx), thisBlockSize ) < thisBlockSize )
throw Exception::BadStream( m_filename );
curidx += thisBlockSize;
Yield( 3 );
} while( curidx < m_src_buffer->GetSizeInBytes() );
Console.WriteLn( "(gzipThread) Data saved to disk without error." );
}
void CompressThread_gzip::OnCleanupInThread()
{
wxGetApp().DeleteThread( this );
}

View File

@ -0,0 +1,17 @@
/* 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 te 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"

View File

@ -21,7 +21,10 @@
#include <wx/imaglist.h>
#include <wx/apptrait.h>
#include "pxEventThread.h"
#include "AppCommon.h"
#include "AppCoreThread.h"
#include "RecentIsoList.h"
#include "System.h"
@ -32,22 +35,12 @@
class Pcsx2App;
typedef void FnType_OnThreadComplete(const wxCommandEvent& evt);
typedef void (Pcsx2App::*FnPtr_AppMethod)();
typedef void (Pcsx2App::*FnPtr_Pcsx2App)();
BEGIN_DECLARE_EVENT_TYPES()
/*DECLARE_EVENT_TYPE( pxEVT_ReloadPlugins, -1 )
DECLARE_EVENT_TYPE( pxEVT_OpenGsPanel, -1 )*/
DECLARE_EVENT_TYPE( pxEvt_FreezeThreadFinished, -1 )
DECLARE_EVENT_TYPE( pxEvt_CoreThreadStatus, -1 )
DECLARE_EVENT_TYPE( pxEvt_LoadPluginsComplete, -1 )
DECLARE_EVENT_TYPE( pxEvt_PluginStatus, -1 )
DECLARE_EVENT_TYPE( pxEvt_SysExecute, -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 )
DECLARE_EVENT_TYPE( pxEvt_ThreadTaskTimeout_SysExec, -1 )
END_DECLARE_EVENT_TYPES()
// This is used when the GS plugin is handling its own window. Messages from the PAD
@ -91,15 +84,14 @@ enum MenuIdentifiers
MenuId_Boot_Iso, // Opens submenu with Iso browser, and recent isos.
MenuId_IsoSelector, // Contains a submenu of selectable "favorite" isos
MenuId_IsoBrowse, // Open dialog, runs selected iso.
MenuId_Boot_CDVD, // opens a submenu filled by CDVD plugin (usually list of drives)
MenuId_Boot_CDVD,
MenuId_Boot_CDVD2,
MenuId_Boot_ELF,
MenuId_Boot_Recent, // Menu populated with recent source bootings
MenuId_SkipBiosToggle, // enables the Bios Skip speedhack
MenuId_Sys_SuspendResume, // suspends/resumes active emulation, retains plugin states
MenuId_Sys_Close, // Closes the emulator (states are preserved)
MenuId_Sys_Reset, // Issues a complete VM reset (wipes preserved states)
MenuId_Sys_Restart, // Issues a complete VM reset (wipes preserved states)
MenuId_Sys_Shutdown, // Closes virtual machine, shuts down plugins, wipes states.
MenuId_Sys_LoadStates, // Opens load states submenu
MenuId_Sys_SaveStates, // Opens save states submenu
@ -284,7 +276,7 @@ struct AppImageIds
{
Paths = Plugins =
Speedhacks = Gamefixes =
Video = Cpu =
Video = Cpu =
MemoryCard = -1;
}
} Config;
@ -384,7 +376,7 @@ protected:
public:
void AddListener( IEventListener_Plugins& listener )
{
m_evtsrc_CorePluginStatus.Add( listener );
m_evtsrc_CorePluginStatus.Add( listener );
}
void AddListener( IEventListener_CoreThread& listener )
@ -399,7 +391,7 @@ public:
void RemoveListener( IEventListener_Plugins& listener )
{
m_evtsrc_CorePluginStatus.Remove( listener );
m_evtsrc_CorePluginStatus.Remove( listener );
}
void RemoveListener( IEventListener_CoreThread& listener )
@ -414,7 +406,7 @@ public:
void AddListener( IEventListener_Plugins* listener )
{
m_evtsrc_CorePluginStatus.Add( listener );
m_evtsrc_CorePluginStatus.Add( listener );
}
void AddListener( IEventListener_CoreThread* listener )
@ -429,7 +421,7 @@ public:
void RemoveListener( IEventListener_Plugins* listener )
{
m_evtsrc_CorePluginStatus.Remove( listener );
m_evtsrc_CorePluginStatus.Remove( listener );
}
void RemoveListener( IEventListener_CoreThread* listener )
@ -441,27 +433,17 @@ public:
{
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 ) );
}
void DispatchEvent( PluginEventType evt );
void DispatchEvent( AppEventType evt );
void DispatchEvent( CoreThreadStatus evt );
void DispatchEvent( IniInterface& ini );
// ----------------------------------------------------------------------------
protected:
int m_PendingSaves;
bool m_ScheduledTermination;
public:
FramerateManager FpsManager;
CommandDictionary GlobalCommands;
@ -474,9 +456,13 @@ protected:
ScopedPtr<RecentIsoList> m_RecentIsoList;
ScopedPtr<pxAppResources> m_Resources;
// Executor Thread for complex VM/System tasks. This thread is used to execute such tasks
// in parallel to the main message pump, to allow the main pump to run without fear of
// blocked threads stalling the GUI.
public:
ExecutorThread SysExecutorThread;
ScopedPtr<SysCoreAllocations> m_CoreAllocs;
ScopedPtr<PluginManager> m_CorePlugins;
protected:
wxWindowID m_id_MainFrame;
@ -489,17 +475,13 @@ public:
Pcsx2App();
virtual ~Pcsx2App();
void PostPluginStatus( PluginEventType pevt );
void PostMenuAction( MenuIdentifiers menu_id ) const;
int IssueDialogAsModal( const wxString& dlgName );
void PostMethod( FnPtr_AppMethod method );
void PostIdleMethod( FnPtr_AppMethod method );
int DoStuckThread( PersistentThread& stuck_thread );
void PostAppMethod( FnPtr_Pcsx2App method );
void PostIdleAppMethod( FnPtr_Pcsx2App method );
void SysExecute();
void SysExecute( CDVD_SourceType cdvdsrc, const wxString& elf_override=wxEmptyString );
void SysReset();
void ReloadPlugins();
void SysShutdown();
void LogicalVsync();
GSFrame& GetGsFrame() const;
@ -528,6 +510,8 @@ public:
void WipeUserModeSettings();
void ReadUserModeSettings();
void StartPendingSave();
void ClearPendingSave();
// --------------------------------------------------------------------------
// App-wide Resources
@ -576,8 +560,8 @@ public:
void OnProgramLogClosed( wxWindowID id );
protected:
bool InvokeMethodOnMainThread( FnPtr_AppMethod method );
bool PostMethodToMainThread( FnPtr_AppMethod method );
bool InvokeOnMainThread( FnPtr_Pcsx2App method );
bool PostAppMethodMyself( FnPtr_Pcsx2App method );
void AllocateCoreStuffs();
void InitDefaultGlobalAccelerators();
@ -586,23 +570,13 @@ protected:
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);
void OnSysExecute( wxCommandEvent& evt );
void OnLoadPluginsComplete( wxCommandEvent& evt );
void OnPluginStatus( 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( pxInvokeAppMethodEvent& evt );
void OnSysExecutorTaskTimeout( wxTimerEvent& evt );
// ----------------------------------------------------------------------------
// Override wx default exception handling behavior
@ -620,40 +594,6 @@ protected:
};
// --------------------------------------------------------------------------------------
// AppCoreThread class
// --------------------------------------------------------------------------------------
class AppCoreThread : public SysCoreThread
{
typedef SysCoreThread _parent;
public:
AppCoreThread();
virtual ~AppCoreThread() throw();
virtual bool Suspend( bool isBlocking=true );
virtual void Resume();
virtual void Reset();
virtual void Cancel( bool isBlocking=true );
virtual void StateCheckInThread();
virtual void ApplySettings( const Pcsx2Config& src );
virtual void ChangeCdvdSource( CDVD_SourceType type );
protected:
virtual void OnResumeReady();
virtual void OnResumeInThread( bool IsSuspended );
virtual void OnSuspendInThread();
virtual void OnCleanupInThread();
//virtual void VsyncInThread();
virtual void PostVsyncToUI();
virtual void ExecuteTaskInThread();
virtual void DoCpuReset();
virtual void DoThreadDeadlocked();
virtual void CpuInitializeMess();
};
DECLARE_APP(Pcsx2App)
// --------------------------------------------------------------------------------------
@ -686,9 +626,6 @@ DECLARE_APP(Pcsx2App)
#define sMainFrame \
if( MainEmuFrame* __frame_ = GetMainFramePtr() ) (*__frame_)
#define sGSFrame \
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. :)
#define sMenuBar \
@ -706,30 +643,6 @@ void AppOpenDialog( wxWindow* parent )
(new DialogType( parent ))->Show();
}
// --------------------------------------------------------------------------------------
// SaveSinglePluginHelper
// --------------------------------------------------------------------------------------
// A scoped convenience class for closing a single plugin and saving its state to memory.
// Emulation is suspended as needed, and is restored when the object leaves scope. Within
// the scope of the object, code is free to call plugin re-configurations or even unload
// a plugin entirely and re-load a different plugin in its place.
//
class SaveSinglePluginHelper
{
protected:
SafeArray<u8> m_plugstore;
const SafeArray<u8>* m_whereitsat;
bool m_resume;
bool m_validstate;
PluginsEnum_t m_pid;
public:
SaveSinglePluginHelper( PluginsEnum_t pid );
virtual ~SaveSinglePluginHelper() throw();
};
extern pxDoAssertFnType AppDoAssert;
// --------------------------------------------------------------------------------------
@ -737,9 +650,10 @@ extern pxDoAssertFnType AppDoAssert;
// --------------------------------------------------------------------------------------
extern int EnumeratePluginsInFolder( const wxDirName& searchPath, wxArrayString* dest );
extern void LoadPluginsPassive( FnType_OnThreadComplete* onComplete );
extern void LoadPluginsPassive();
extern void LoadPluginsImmediate();
extern void UnloadPlugins();
extern void ShutdownPlugins();
extern void AppLoadSettings();
extern void AppSaveSettings();
@ -755,5 +669,22 @@ extern MainEmuFrame* GetMainFramePtr();
extern __aligned16 AppCoreThread CoreThread;
extern __aligned16 SysMtgsThread mtgsThread;
extern __aligned16 AppPluginManager CorePlugins;
extern void UI_UpdateSysControls();
extern void UI_DisableSysActions();
extern void UI_EnableSysActions();
extern void UI_DisableSysReset();
extern void UI_DisableSysShutdown();
#define AffinityAssert_AllowFrom_SysExecutor() \
pxAssertMsg( wxGetApp().SysExecutorThread.IsSelf(), "Thread affinity violation: Call allowed from SysExecutor thread only." )
#define AffinityAssert_DisallowFrom_SysExecutor() \
pxAssertMsg( !wxGetApp().SysExecutorThread.IsSelf(), "Thread affinity violation: Call is *not* allowed from SysExecutor thread." )
extern ExecutorThread& GetSysExecutorThread();

View File

@ -93,9 +93,9 @@ namespace PathDefs
// Fetches the path location for user-consumable documents -- stuff users are likely to want to
// share with other programs: screenshots, memory cards, and savestates.
wxDirName GetDocuments()
wxDirName GetDocuments( DocsModeType mode )
{
switch( DocsFolderMode )
switch( mode )
{
case DocsFolder_User: return (wxDirName)Path::Combine( wxStandardPaths::Get().GetDocumentsDir(), wxGetApp().GetAppName() );
case DocsFolder_CWD: return (wxDirName)wxGetCwd();
@ -106,6 +106,11 @@ namespace PathDefs
return wxDirName();
}
wxDirName GetDocuments()
{
return GetDocuments( DocsFolderMode );
}
wxDirName GetSnapshots()
{

View File

@ -23,15 +23,25 @@ enum DocsModeType
{
// uses /home/user or /cwd for the program data
DocsFolder_User,
// uses the current working directory for program data
DocsFolder_CWD,
// uses a custom location for program data
DocsFolder_Custom,
};
extern DocsModeType DocsFolderMode; //
namespace PathDefs
{
// complete pathnames are returned by these functions
// For 99% of all code, you should use these.
extern wxDirName GetDocuments();
extern wxDirName GetDocuments( DocsModeType mode );
extern wxDirName GetThemes();
}
extern DocsModeType DocsFolderMode; //
extern wxDirName SettingsFolder; // dictates where the settings folder comes from, *if* UseDefaultSettingsFolder is FALSE.
extern wxDirName CustomDocumentsFolder; // allows the specification of a custom home folder for PCSX2 documents files.
extern bool UseDefaultSettingsFolder; // when TRUE, pcsx2 derives the settings folder from the UseAdminMode
@ -48,7 +58,7 @@ enum AspectRatioType
};
// =====================================================================================================
// Pcsx2 Application Configuration.
// Pcsx2 Application Configuration.
// =====================================================================================================
class AppConfig
@ -161,7 +171,7 @@ public:
Fixed100 SlomoScalar;
FramerateOptions();
void LoadSave( IniInterface& conf );
void SanityCheck();
};
@ -212,7 +222,7 @@ public:
FilenameOptions BaseFilenames;
GSWindowOptions GSWindow;
FramerateOptions Framerate;
// PCSX2-core emulation options, which are passed to the emu core prior to initiating
// an emulation session. Note these are the options saved into the GUI ini file and
// which are shown as options in the gui preferences, but *not* necessarily the options
@ -227,7 +237,7 @@ public:
wxString FullpathTo( PluginsEnum_t pluginId ) const;
bool FullpathMatchTest( PluginsEnum_t pluginId, const wxString& cmpto ) const;
void LoadSaveUserMode( IniInterface& ini, const wxString& cwdhash );
void LoadSave( IniInterface& ini );

View File

@ -0,0 +1,419 @@
/* 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 "AppSaveStates.h"
#include "GSFrame.h"
#include <wx/dir.h>
#include <wx/file.h>
#include "Plugins.h"
#include "GS.h"
#include "HostGui.h"
#include "AppConfig.h"
using namespace Threading;
// 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;
__aligned16 AppPluginManager CorePlugins;
PluginManager& GetCorePlugins()
{
return CorePlugins;
}
// --------------------------------------------------------------------------------------
// CorePluginsEvent
// --------------------------------------------------------------------------------------
class CorePluginsEvent : public pxInvokeActionEvent
{
typedef pxInvokeActionEvent _parent;
protected:
PluginEventType m_evt;
public:
virtual ~CorePluginsEvent() throw() {}
CorePluginsEvent* Clone() const { return new CorePluginsEvent( *this ); }
explicit CorePluginsEvent( PluginEventType evt, SynchronousActionState* sema=NULL )
: pxInvokeActionEvent( sema )
{
m_evt = evt;
}
explicit CorePluginsEvent( PluginEventType evt, SynchronousActionState& sema )
: pxInvokeActionEvent( sema )
{
m_evt = evt;
}
CorePluginsEvent( const CorePluginsEvent& src )
: pxInvokeActionEvent( src )
{
m_evt = src.m_evt;
}
void SetEventType( PluginEventType evt ) { m_evt = evt; }
PluginEventType GetEventType() { return m_evt; }
protected:
void _DoInvoke()
{
sApp.DispatchEvent( m_evt );
}
};
static void PostPluginStatus( PluginEventType pevt )
{
sApp.PostAction( CorePluginsEvent( pevt ) );
}
static void ConvertPluginFilenames( wxString (&passins)[PluginId_Count] )
{
const PluginInfo* pi = tbl_PluginInfo; do
{
passins[pi->id] = OverrideOptions.Filenames[pi->id].GetFullPath();
if( passins[pi->id].IsEmpty() || !wxFileExists( passins[pi->id] ) )
passins[pi->id] = g_Conf->FullpathTo( pi->id );
} while( ++pi, pi->shortname != NULL );
}
// --------------------------------------------------------------------------------------
// AppPluginManager
// --------------------------------------------------------------------------------------
AppPluginManager::AppPluginManager()
{
}
AppPluginManager::~AppPluginManager() throw()
{
}
void AppPluginManager::Load( const wxString (&folders)[PluginId_Count] )
{
if( !pxAssert(!AreLoaded()) ) return;
SetSettingsFolder( GetSettingsFolder().ToString() );
_parent::Load( folders );
PostPluginStatus( CorePlugins_Loaded );
}
void AppPluginManager::Unload()
{
_parent::Unload();
PostPluginStatus( CorePlugins_Unloaded );
}
void AppPluginManager::Init()
{
SetSettingsFolder( GetSettingsFolder().ToString() );
_parent::Init();
PostPluginStatus( CorePlugins_Init );
}
void AppPluginManager::Shutdown()
{
_parent::Shutdown();
PostPluginStatus( CorePlugins_Shutdown );
}
typedef void (AppPluginManager::*FnPtr_AppPluginManager)();
class SysExecEvent_AppPluginManager : public SysExecEvent
{
protected:
FnPtr_AppPluginManager m_method;
public:
virtual ~SysExecEvent_AppPluginManager() throw() {}
SysExecEvent_AppPluginManager* Clone() const { return new SysExecEvent_AppPluginManager( *this ); }
SysExecEvent_AppPluginManager( FnPtr_AppPluginManager method )
{
m_method = method;
}
protected:
void _DoInvoke()
{
if( m_method ) (CorePlugins.*m_method)();
}
};
void AppPluginManager::Close()
{
AffinityAssert_AllowFrom_CoreThread();
/*if( !GetSysExecutorThread().IsSelf() )
{
GetSysExecutorThread().ProcessEvent( new SysExecEvent_AppPluginManager( &AppPluginManager::Close ) );
return;
}*/
if( !NeedsClose() ) return;
PostPluginStatus( CorePlugins_Closing );
_parent::Close();
PostPluginStatus( CorePlugins_Closed );
}
void AppPluginManager::Open()
{
AffinityAssert_AllowFrom_CoreThread();
/*if( !GetSysExecutorThread().IsSelf() )
{
GetSysExecutorThread().ProcessEvent( new SysExecEvent_AppPluginManager( &AppPluginManager::Open ) );
return;
}*/
SetSettingsFolder( GetSettingsFolder().ToString() );
if( !NeedsOpen() ) return;
PostPluginStatus( CorePlugins_Opening );
_parent::Open();
PostPluginStatus( CorePlugins_Opened );
}
// Yay, this plugin is guaranteed to always be opened first and closed last.
bool AppPluginManager::OpenPlugin_GS()
{
if( GSopen2 && !s_DisableGsWindow )
{
sApp.OpenGsPanel();
}
bool retval = _parent::OpenPlugin_GS();
if( g_LimiterMode == Limit_Turbo )
GSsetVsync( false );
return retval;
}
static int _guard = 0;
// Yay, this plugin is guaranteed to always be opened first and closed last.
void AppPluginManager::ClosePlugin_GS()
{
/*if( GSopen2 == NULL || CloseViewportWithPlugins )
{
// All other plugins must be closed before the GS, because they all rely on
// the GS window handle being valid. The recursion guard will protect this
// function from being called a million times. ;)
RecursionGuard mess( _guard );
if( !mess.IsReentrant() ) Close();
}*/
_parent::ClosePlugin_GS();
if( GetMTGS().IsSelf() && GSopen2 && CloseViewportWithPlugins ) sApp.CloseGsPanel();
}
// --------------------------------------------------------------------------------------
// LoadCorePluginsEvent
// --------------------------------------------------------------------------------------
class LoadCorePluginsEvent : public SysExecEvent
{
protected:
wxString m_folders[PluginId_Count];
public:
LoadCorePluginsEvent()
{
ConvertPluginFilenames( m_folders );
}
wxString GetEventName() const
{
return L"LoadCorePlugins";
}
wxString GetEventMessage() const
{
return _("Loading PS2 system plugins...");
}
protected:
void _DoInvoke()
{
CorePlugins.Load( m_folders );
}
};
// --------------------------------------------------------------------------------------
// Public API / Interface
// --------------------------------------------------------------------------------------
int EnumeratePluginsInFolder( const wxDirName& searchpath, wxArrayString* dest )
{
ScopedPtr<wxArrayString> placebo;
wxArrayString* realdest = dest;
if( realdest == NULL )
placebo = realdest = new wxArrayString(); // placebo is our /dev/null -- gets deleted when done
#ifdef __WXMSW__
// Windows pretty well has a strict "must end in .dll" rule.
wxString pattern( L"*%s" );
#else
// Other platforms seem to like to version their libs after the .so extension:
// blah.so.3.1.fail?
wxString pattern( L"*%s*" );
#endif
return searchpath.Exists() ?
wxDir::GetAllFiles( searchpath.ToString(), realdest, wxsFormat( pattern, wxDynamicLibrary::GetDllExt()), wxDIR_FILES ) : 0;
}
// Posts a message to the App to reload plugins. Plugins are loaded via a background thread
// which is started on a pending event, so don't expect them to be ready "right now."
// If plugins are already loaded, onComplete is invoked, and the function returns with no
// other actions performed.
void LoadPluginsPassive()
{
AffinityAssert_AllowFrom_MainUI();
// Plugins already loaded?
if( !CorePlugins.AreLoaded() )
{
wxGetApp().SysExecutorThread.PostEvent( new LoadCorePluginsEvent() );
}
}
static void _LoadPluginsImmediate()
{
if( CorePlugins.AreLoaded() ) return;
wxString passins[PluginId_Count];
ConvertPluginFilenames( passins );
CorePlugins.Load( passins );
}
void LoadPluginsImmediate()
{
AffinityAssert_AllowFrom_SysExecutor();
_LoadPluginsImmediate();
}
// Performs a blocking load of plugins. If the emulation thread is active, it is shut down
// automatically to prevent race conditions (it depends on plugins).
//
// Exceptions regarding plugin failures will propagate out of this function, so be prepared
// to handle them.
//
// Note that this is not recommended for most situations, but coding improper passive loads
// is probably worse, so if in doubt use this and air will fix it up for you later. :)
//
void ScopedCoreThreadClose::LoadPlugins()
{
DbgCon.WriteLn("(ScopedCoreThreadClose) Loading plugins!");
_LoadPluginsImmediate();
}
class SysExecEvent_UnloadPlugins : public SysExecEvent
{
public:
virtual ~SysExecEvent_UnloadPlugins() throw() {}
SysExecEvent_UnloadPlugins* Clone() const { return new SysExecEvent_UnloadPlugins(*this); }
virtual bool AllowCancelOnExit() const { return false; }
virtual bool IsCriticalEvent() const { return true; }
void _DoInvoke()
{
CoreThread.Cancel();
CorePlugins.Unload();
}
};
class SysExecEvent_ShutdownPlugins : public SysExecEvent
{
public:
virtual ~SysExecEvent_ShutdownPlugins() throw() {}
SysExecEvent_ShutdownPlugins* Clone() const { return new SysExecEvent_ShutdownPlugins(*this); }
virtual bool AllowCancelOnExit() const { return false; }
virtual bool IsCriticalEvent() const { return true; }
void _DoInvoke()
{
CoreThread.Cancel();
CorePlugins.Shutdown();
}
};
void UnloadPlugins()
{
GetSysExecutorThread().PostEvent( new SysExecEvent_UnloadPlugins() );
}
void ShutdownPlugins()
{
GetSysExecutorThread().PostEvent( new SysExecEvent_ShutdownPlugins() );
}
// --------------------------------------------------------------------------------------
// SaveSinglePluginHelper (Implementations)
// --------------------------------------------------------------------------------------
SaveSinglePluginHelper::SaveSinglePluginHelper( PluginsEnum_t pid )
: m_plugstore( L"PluginConf Savestate" )
{
s_DisableGsWindow = true;
m_pid = pid;
m_validstate = SysHasValidState();
_LoadPluginsImmediate();
if( !CorePlugins.AreLoaded() ) return;
if( !m_validstate ) return;
Console.WriteLn( Color_Green, L"Suspending single plugin: " + tbl_PluginInfo[m_pid].GetShortname() );
memSavingState save( m_plugstore );
GetCorePlugins().Freeze( m_pid, save );
GetCorePlugins().Close( pid );
}
SaveSinglePluginHelper::~SaveSinglePluginHelper() throw()
{
try
{
if( m_validstate )
{
Console.WriteLn( Color_Green, L"Recovering single plugin: " + tbl_PluginInfo[m_pid].GetShortname() );
memLoadingState load( m_plugstore );
//if( m_plugstore.IsDisposed() ) load.SeekToSection( m_pid );
GetCorePlugins().Freeze( m_pid, load );
GetCorePlugins().Close( m_pid );
}
s_DisableGsWindow = false;
}
DESTRUCTOR_CATCHALL;
s_DisableGsWindow = false;
}

View File

@ -0,0 +1,48 @@
/* 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 "AppCommon.h"
// --------------------------------------------------------------------------------------
// AppPluginManager
// --------------------------------------------------------------------------------------
// This extension of PluginManager provides event listener sources for plugins -- loading,
// unloading, open, close, shutdown, etc.
//
// FIXME : Should this be made part of the PCSX2 core emulation? (integrated into PluginManager)
// I'm undecided on if it makes sense more in that context or in this one (interface).
//
class AppPluginManager : public PluginManager
{
typedef PluginManager _parent;
public:
AppPluginManager();
virtual ~AppPluginManager() throw();
void Load( const wxString (&folders)[PluginId_Count] );
void Unload();
void Init();
void Shutdown();
void Close();
void Open();
protected:
bool OpenPlugin_GS();
void ClosePlugin_GS();
};

View File

@ -14,78 +14,104 @@
*/
#include "PrecompiledHeader.h"
#include "MainFrame.h"
#include "ps2/BiosTools.h"
#include "App.h"
#include "AppSaveStates.h"
#include "ps2/BiosTools.h"
#include "GS.h"
__aligned16 SysMtgsThread mtgsThread;
__aligned16 AppCoreThread CoreThread;
static void PostCoreStatus( CoreThreadStatus pevt )
{
sApp.PostAction( CoreThreadStatusEvent( pevt ) );
}
// --------------------------------------------------------------------------------------
// AppCoreThread Implementations
// --------------------------------------------------------------------------------------
AppCoreThread::AppCoreThread() : SysCoreThread()
{
}
AppCoreThread::~AppCoreThread() throw()
{
AppCoreThread::Cancel();
_parent::Cancel(); // use parent's, skips thread affinity check.
}
void AppCoreThread::Cancel( bool isBlocking )
{
if( !_parent::Cancel( wxTimeSpan( 0, 0, 2, 0 ) ) )
{
// Possible deadlock!
throw Exception::ThreadDeadlock( this );
}
AffinityAssert_AllowFrom_SysExecutor();
_parent::Cancel( wxTimeSpan(0, 0, 2, 0) );
}
void AppCoreThread::Reset()
void AppCoreThread::Shutdown()
{
ScopedBusyCursor::SetDefault( Cursor_KindaBusy );
AffinityAssert_AllowFrom_SysExecutor();
_parent::Reset();
CorePlugins.Shutdown();
}
void AppCoreThread::DoThreadDeadlocked()
ExecutorThread& GetSysExecutorThread()
{
wxGetApp().DoStuckThread( *this );
return wxGetApp().SysExecutorThread;
}
bool AppCoreThread::Suspend( bool isBlocking )
typedef void (AppCoreThread::*FnPtr_CoreThreadMethod)();
// --------------------------------------------------------------------------------------
// SysExecEvent_InvokeCoreThreadMethod
// --------------------------------------------------------------------------------------
class SysExecEvent_InvokeCoreThreadMethod : public SysExecEvent
{
ScopedBusyCursor::SetDefault( Cursor_KindaBusy );
protected:
FnPtr_CoreThreadMethod m_method;
bool retval = _parent::Suspend( false );
if( !retval || isBlocking )
ScopedBusyCursor::SetDefault( Cursor_NotBusy );
if( g_Conf->GSWindow.CloseOnEsc )
public:
virtual ~SysExecEvent_InvokeCoreThreadMethod() throw() {}
SysExecEvent_InvokeCoreThreadMethod* Clone() const { return new SysExecEvent_InvokeCoreThreadMethod(*this); }
SysExecEvent_InvokeCoreThreadMethod( FnPtr_CoreThreadMethod method )
{
sGSFrame.Hide();
m_method = method;
}
protected:
void _DoInvoke()
{
if( m_method ) (CoreThread.*m_method)();
}
};
return retval;
bool ProcessingMethodViaThread( FnPtr_CoreThreadMethod method )
{
if( GetSysExecutorThread().IsSelf() ) return false;
SysExecEvent_InvokeCoreThreadMethod evt( method );
GetSysExecutorThread().ProcessEvent( evt );
return false;
}
static void _Suspend()
{
GetCoreThread().Suspend(true);
}
void AppCoreThread::Suspend( bool isBlocking )
{
if( !GetSysExecutorThread().SelfProcessMethod( _Suspend ) )
_parent::Suspend(true);
}
static int resume_tries = 0;
void AppCoreThread::Resume()
{
// Thread control (suspend / resume) should only be performed from the main/gui thread.
if( !AffinityAssert_AllowFromMain() ) return;
if( m_ExecMode == ExecMode_Opened ) return;
if( m_ResumeProtection.IsLocked() ) return;
if( !AffinityAssert_AllowFrom_SysExecutor() ) return;
if( m_ExecMode == ExecMode_Opened || (m_CloseTemporary > 0) ) return;
if( !pxAssert( g_plugins != NULL ) ) return;
if( !pxAssert( CorePlugins.AreLoaded() ) ) return;
if( sys_resume_lock > 0 )
{
Console.WriteLn( "SysResume: State is locked, ignoring Resume request!" );
return;
}
ScopedBusyCursor::SetDefault( Cursor_KindaBusy );
_parent::Resume();
if( m_ExecMode != ExecMode_Opened )
@ -93,7 +119,7 @@ void AppCoreThread::Resume()
// Resume failed for some reason, so update GUI statuses and post a message to
// try again on the resume.
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreThread_Suspended );
PostCoreStatus( CoreThread_Suspended );
if( (m_ExecMode != ExecMode_Closing) || (m_ExecMode != ExecMode_Pausing) )
{
@ -109,29 +135,30 @@ void AppCoreThread::Resume()
resume_tries = 0;
}
void AppCoreThread::ChangeCdvdSource( CDVD_SourceType type )
void AppCoreThread::ChangeCdvdSource()
{
g_Conf->CdvdSource = type;
_parent::ChangeCdvdSource( type );
sMainFrame.UpdateIsoSrcSelection();
if( !GetSysExecutorThread().IsSelf() )
{
GetSysExecutorThread().PostEvent( new SysExecEvent_InvokeCoreThreadMethod(&AppCoreThread::ChangeCdvdSource) );
return;
}
CDVD_SourceType cdvdsrc( g_Conf->CdvdSource );
if( cdvdsrc == CDVDsys_GetSourceType() ) return;
// Fast change of the CDVD source only -- a Pause will suffice.
ScopedCoreThreadPause paused_core;
GetCorePlugins().Close( PluginId_CDVD );
CDVDsys_ChangeSource( cdvdsrc );
paused_core.AllowResume();
// TODO: Add a listener for CDVDsource changes? Or should we bother?
}
void AppCoreThread::DoCpuReset()
{
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreThread_Reset );
_parent::DoCpuReset();
}
void AppCoreThread::OnResumeReady()
{
ApplySettings( g_Conf->EmuOptions );
if( !wxFile::Exists( g_Conf->CurrentIso ) )
g_Conf->CurrentIso.Clear();
sApp.GetRecentIsoManager().Add( g_Conf->CurrentIso );
CDVDsys_SetFile( CDVDsrc_Iso, g_Conf->CurrentIso );
AppSaveSettings();
@ -139,45 +166,8 @@ void AppCoreThread::OnResumeReady()
_parent::OnResumeReady();
}
void AppCoreThread::OnResumeInThread( bool isSuspended )
{
_parent::OnResumeInThread( isSuspended );
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreThread_Resumed );
}
void AppCoreThread::OnSuspendInThread()
{
_parent::OnSuspendInThread();
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreThread_Suspended );
}
// Called whenever the thread has terminated, for either regular or irregular reasons.
// Typically the thread handles all its own errors, so there's no need to have error
// handling here. However it's a good idea to update the status of the GUI to reflect
// the new (lack of) thread status, so this posts a message to the App to do so.
void AppCoreThread::OnCleanupInThread()
{
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreThread_Stopped );
_parent::OnCleanupInThread();
}
void AppCoreThread::PostVsyncToUI()
{
wxGetApp().LogicalVsync();
}
void AppCoreThread::StateCheckInThread()
{
_parent::StateCheckInThread();
}
// To simplify settings application rules and re-entry conditions, the main App's implementation
// of ApplySettings requires that the caller manually ensure that the thread has been properly
// suspended. If the thread has not been suspended, this call will fail *silently*.
void AppCoreThread::ApplySettings( const Pcsx2Config& src )
{
//if( m_ExecMode != ExecMode_Closed ) return;
Pcsx2Config fixup( src );
if( !g_Conf->EnableSpeedHacks )
fixup.Speedhacks = Pcsx2Config::SpeedhackOptions();
@ -192,9 +182,63 @@ void AppCoreThread::ApplySettings( const Pcsx2Config& src )
RecursionGuard guard( localc );
if( guard.IsReentrant() ) return;
if( fixup == EmuConfig ) return;
_parent::ApplySettings( fixup );
if( m_ExecMode >= ExecMode_Opened )
{
ScopedCoreThreadPause paused_core;
_parent::ApplySettings( fixup );
paused_core.AllowResume();
}
else
{
_parent::ApplySettings( fixup );
}
}
// --------------------------------------------------------------------------------------
// AppCoreThread *Worker* Implementations
// (Called from the context of this thread only)
// --------------------------------------------------------------------------------------
void AppCoreThread::DoCpuReset()
{
PostCoreStatus( CoreThread_Reset );
_parent::DoCpuReset();
}
void AppCoreThread::OnResumeInThread( bool isSuspended )
{
_parent::OnResumeInThread( isSuspended );
PostCoreStatus( CoreThread_Resumed );
}
void AppCoreThread::OnSuspendInThread()
{
_parent::OnSuspendInThread();
PostCoreStatus( CoreThread_Suspended );
}
// Called whenever the thread has terminated, for either regular or irregular reasons.
// Typically the thread handles all its own errors, so there's no need to have error
// handling here. However it's a good idea to update the status of the GUI to reflect
// the new (lack of) thread status, so this posts a message to the App to do so.
void AppCoreThread::OnCleanupInThread()
{
PostCoreStatus( CoreThread_Stopped );
_parent::OnCleanupInThread();
}
void AppCoreThread::PostVsyncToUI()
{
wxGetApp().LogicalVsync();
}
void AppCoreThread::StateCheckInThread()
{
_parent::StateCheckInThread();
}
// Thread Affinity: This function is called from the SysCoreThread. :)
void AppCoreThread::CpuInitializeMess()
{
if( m_hasValidState ) return;
@ -205,33 +249,278 @@ void AppCoreThread::CpuInitializeMess()
// in order to ensure the plugins are in the proper (loaded/opened) state.
SysClearExecutionCache();
StateCopy_ThawFromMem_Blocking();
memLoadingState( StateCopy_GetBuffer() ).FreezeAll();
StateCopy_Clear();
m_hasValidState = true;
m_resetVirtualMachine = false;
return;
}
_parent::CpuInitializeMess();
}
void AppCoreThread::ExecuteTaskInThread()
{
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreThread_Started );
PostCoreStatus( CoreThread_Started );
_parent::ExecuteTaskInThread();
// ----------------------------------------------------------------------------
/*catch( Exception::PluginError& ex )
{
if( g_plugins != NULL ) g_plugins->Close();
Console.Error( ex.FormatDiagnosticMessage() );
Msgbox::Alert( ex.FormatDisplayMessage(), _("Plugin Open Error") );
if( HandlePluginError( ex ) )
{
// fixme: automatically re-try emu startup here...
}
}*/
}
enum
{
FullStop_BlockingResume
, FullStop_NonblockingResume
, FullStop_SkipResume
};
// --------------------------------------------------------------------------------------
// SysExecEvent_FullStop
// --------------------------------------------------------------------------------------
class SysExecEvent_FullStop : public SysExecEvent
{
protected:
SynchronousActionState* m_resume;
Threading::Mutex* m_mtx_resume;
public:
virtual ~SysExecEvent_FullStop() throw() {}
SysExecEvent_FullStop* Clone() const
{
return new SysExecEvent_FullStop( *this );
}
SysExecEvent_FullStop( SynchronousActionState* sync=NULL, SynchronousActionState* resume_sync=NULL, Threading::Mutex* mtx_resume=NULL )
: SysExecEvent( sync )
{
m_resume = resume_sync;
m_mtx_resume = mtx_resume;
}
SysExecEvent_FullStop( SynchronousActionState& sync, SynchronousActionState& resume_sync, Threading::Mutex& mtx_resume )
: SysExecEvent( sync )
{
m_resume = &resume_sync;
m_mtx_resume = &mtx_resume;
}
protected:
void _DoInvoke()
{
ScopedCoreThreadClose closed_core;
PostResult();
if( m_resume )
{
ScopedLock lock( m_mtx_resume );
// If the sender of the message requests a non-blocking resume, then we need
// to deallocate the m_sync object, since the sender will likely leave scope and
// invalidate it.
switch( m_resume->WaitForResult() )
{
case FullStop_SkipResume: return;
case FullStop_BlockingResume:
if( m_sync ) m_sync->ClearResult();
break;
case FullStop_NonblockingResume:
m_sync = NULL;
break;
}
}
closed_core.AllowResume();
}
};
// --------------------------------------------------------------------------------------
// SysExecEvent_FullStop
// --------------------------------------------------------------------------------------
class SysExecEvent_Pause : public SysExecEvent
{
protected:
SynchronousActionState* m_resume;
Threading::Mutex* m_mtx_resume;
public:
virtual ~SysExecEvent_Pause() throw() {}
SysExecEvent_Pause* Clone() const
{
return new SysExecEvent_Pause( *this );
}
SysExecEvent_Pause( SynchronousActionState* sync=NULL, SynchronousActionState* resume_sync=NULL, Threading::Mutex* mtx_resume=NULL )
: SysExecEvent( sync )
{
m_resume = resume_sync;
m_mtx_resume = mtx_resume;
}
SysExecEvent_Pause( SynchronousActionState& sync, SynchronousActionState& resume_sync, Threading::Mutex& mtx_resume )
: SysExecEvent( sync )
{
m_resume = &resume_sync;
m_mtx_resume = &mtx_resume;
}
protected:
void _DoInvoke()
{
ScopedCoreThreadPause paused_core;
PostResult();
if( m_resume )
{
ScopedLock lock( m_mtx_resume );
// If the sender of the message requests a non-blocking resume, then we need
// to deallocate the m_sync object, since the sender will likely leave scope and
// invalidate it.
switch( m_resume->WaitForResult() )
{
case FullStop_SkipResume: return;
case FullStop_BlockingResume:
if( m_sync ) m_sync->ClearResult();
break;
case FullStop_NonblockingResume:
m_sync = NULL;
break;
}
}
paused_core.AllowResume();
}
};
// --------------------------------------------------------------------------------------
// ScopedCoreThreadClose / ScopedCoreThreadPause
// --------------------------------------------------------------------------------------
static __threadlocal bool ScopedCore_IsPaused = false;
static __threadlocal bool ScopedCore_IsFullyClosed = false;
BaseScopedCoreThread::BaseScopedCoreThread()
{
//AffinityAssert_AllowFrom_MainUI();
m_allowResume = false;
m_alreadyStopped = false;
m_alreadyScoped = false;
}
BaseScopedCoreThread::~BaseScopedCoreThread() throw()
{
}
// Allows the object to resume execution upon object destruction. Typically called as the last thing
// in the object's scope. Any code prior to this call that causes exceptions will not resume the emulator,
// which is *typically* the intended behavior when errors occur.
void BaseScopedCoreThread::AllowResume()
{
m_allowResume = true;
}
void BaseScopedCoreThread::DisallowResume()
{
m_allowResume = false;
}
void BaseScopedCoreThread::DoResume()
{
if( m_alreadyStopped ) return;
if( !GetSysExecutorThread().IsSelf() )
{
//DbgCon.WriteLn("(ScopedCoreThreadPause) Threaded Scope Created!");
m_sync_resume.PostResult( m_allowResume ? FullStop_NonblockingResume : FullStop_SkipResume );
m_mtx_resume.Wait();
}
else
CoreThread.Resume();
}
ScopedCoreThreadClose::ScopedCoreThreadClose()
{
if( ScopedCore_IsFullyClosed )
{
// tracks if we're already in scope or not.
m_alreadyScoped = true;
return;
}
if( !GetSysExecutorThread().IsSelf() )
{
//DbgCon.WriteLn("(ScopedCoreThreadClose) Threaded Scope Created!");
GetSysExecutorThread().PostEvent( SysExecEvent_FullStop(m_sync, m_sync_resume, m_mtx_resume) );
m_sync.WaitForResult();
m_sync.RethrowException();
}
else if( !(m_alreadyStopped = CoreThread.IsClosed()) )
CoreThread.Suspend();
ScopedCore_IsFullyClosed = true;
}
ScopedCoreThreadClose::~ScopedCoreThreadClose() throw()
{
if( m_alreadyScoped ) return;
_parent::DoResume();
ScopedCore_IsFullyClosed = false;
}
ScopedCoreThreadPause::ScopedCoreThreadPause()
{
if( ScopedCore_IsFullyClosed || ScopedCore_IsPaused )
{
// tracks if we're already in scope or not.
m_alreadyScoped = true;
return;
}
if( !GetSysExecutorThread().IsSelf() )
{
//DbgCon.WriteLn("(ScopedCoreThreadPause) Threaded Scope Created!");
GetSysExecutorThread().PostEvent( SysExecEvent_Pause(m_sync, m_sync_resume, m_mtx_resume) );
m_sync.WaitForResult();
m_sync.RethrowException();
}
else if( !(m_alreadyStopped = CoreThread.IsPaused()) )
CoreThread.Pause();
ScopedCore_IsPaused = true;
}
ScopedCoreThreadPause::~ScopedCoreThreadPause() throw()
{
if( m_alreadyScoped ) return;
_parent::DoResume();
ScopedCore_IsPaused = false;
}
ScopedCoreThreadPopup::ScopedCoreThreadPopup()
{
// The old style GUI (without GSopen2) must use a full close of the CoreThread, in order to
// ensure that the GS window isn't blocking the popup, and to avoid crashes if the GS window
// is maximized or fullscreen.
if( !GSopen2 )
m_scoped_core = new ScopedCoreThreadClose();
else
m_scoped_core = new ScopedCoreThreadPause();
};
void ScopedCoreThreadPopup::AllowResume()
{
if( m_scoped_core ) m_scoped_core->AllowResume();
}
void ScopedCoreThreadPopup::DisallowResume()
{
if( m_scoped_core ) m_scoped_core->DisallowResume();
}

143
pcsx2/gui/AppCoreThread.h Normal file
View File

@ -0,0 +1,143 @@
/* 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 "SysThreads.h"
#include "AppCommon.h"
#include "AppCorePlugins.h"
#define AffinityAssert_AllowFrom_CoreThread() \
pxAssertMsg( GetCoreThread().IsSelf(), "Thread affinity violation: Call allowed from SysCoreThread only." )
#define AffinityAssert_DisallowFrom_CoreThread() \
pxAssertMsg( !GetCoreThread().IsSelf(), "Thread affinity violation: Call is *not* allowed from SysCoreThread." )
// --------------------------------------------------------------------------------------
// AppCoreThread class
// --------------------------------------------------------------------------------------
class AppCoreThread : public SysCoreThread
{
typedef SysCoreThread _parent;
public:
AppCoreThread();
virtual ~AppCoreThread() throw();
virtual void Suspend( bool isBlocking=false );
virtual void Resume();
virtual void Shutdown();
virtual void Cancel( bool isBlocking=true );
virtual void StateCheckInThread();
virtual void ApplySettings( const Pcsx2Config& src );
virtual void ChangeCdvdSource();
protected:
virtual void OnResumeReady();
virtual void OnResumeInThread( bool IsSuspended );
virtual void OnSuspendInThread();
virtual void OnCleanupInThread();
virtual void PostVsyncToUI();
virtual void ExecuteTaskInThread();
virtual void DoCpuReset();
virtual void CpuInitializeMess();
};
class IScopedCoreThread
{
protected:
IScopedCoreThread() {}
public:
virtual ~IScopedCoreThread() throw() {};
virtual void AllowResume()=0;
virtual void DisallowResume()=0;
};
class BaseScopedCoreThread : public IScopedCoreThread
{
DeclareNoncopyableObject( BaseScopedCoreThread );
protected:
bool m_allowResume;
bool m_alreadyStopped;
bool m_alreadyScoped;
SynchronousActionState m_sync;
SynchronousActionState m_sync_resume;
Threading::Mutex m_mtx_resume;
BaseScopedCoreThread();
public:
virtual ~BaseScopedCoreThread() throw()=0;
virtual void AllowResume();
virtual void DisallowResume();
protected:
void DoResume();
};
// --------------------------------------------------------------------------------------
// ScopedCoreThreadClose / ScopedCoreThreadPause / ScopedCoreThreadPopup
// --------------------------------------------------------------------------------------
// This class behaves a bit differently from other scoped classes due to the "standard"
// assumption that we actually do *not* want to resume CoreThread operations when an
// exception occurs. Because of this, the destructor of this class does *not* unroll the
// suspend operation. Instead you must manually instruct the class to resume using a call
// to the provisioned AllowResume() method.
//
// If the class leaves scope without having been resumed, a log is written to the console.
// This can be useful for troubleshooting, and also allows the log a second line of info
// indicating the status of CoreThread execution at the time of the exception.
//
// ScopedCoreThreadPopup is intended for use where message boxes are popped up to the user.
// The old style GUI (without GSopen2) must use a full close of the CoreThread, in order to
// ensure that the GS window isn't blocking the popup, and to avoid crashes if the GS window
// is maximized or fullscreen.
//
class ScopedCoreThreadClose : public BaseScopedCoreThread
{
typedef BaseScopedCoreThread _parent;
public:
ScopedCoreThreadClose();
virtual ~ScopedCoreThreadClose() throw();
void LoadPlugins();
};
struct ScopedCoreThreadPause : public BaseScopedCoreThread
{
typedef BaseScopedCoreThread _parent;
public:
ScopedCoreThreadPause();
virtual ~ScopedCoreThreadPause() throw();
};
struct ScopedCoreThreadPopup : public IScopedCoreThread
{
protected:
ScopedPtr<BaseScopedCoreThread> m_scoped_core;
public:
ScopedCoreThreadPopup();
virtual ~ScopedCoreThreadPopup() throw() {}
virtual void AllowResume();
virtual void DisallowResume();
};

View File

@ -16,6 +16,7 @@
#pragma once
#include "Utilities/EventSource.h"
#include "Utilities/pxEvents.h"
enum CoreThreadStatus
{
@ -242,3 +243,28 @@ protected:
virtual void AppStatusEvent_OnSettingsApplied() { Owner.AppStatusEvent_OnSettingsApplied(); }
virtual void AppStatusEvent_OnExit() { Owner.AppStatusEvent_OnExit(); }
};
// --------------------------------------------------------------------------------------
// CoreThreadStatusEvent
// --------------------------------------------------------------------------------------
class CoreThreadStatusEvent : public pxInvokeActionEvent
{
typedef pxInvokeActionEvent _parent;
protected:
CoreThreadStatus m_evt;
public:
virtual ~CoreThreadStatusEvent() throw() {}
CoreThreadStatusEvent* Clone() const { return new CoreThreadStatusEvent( *this ); }
explicit CoreThreadStatusEvent( CoreThreadStatus evt, SynchronousActionState* sema=NULL );
explicit CoreThreadStatusEvent( CoreThreadStatus evt, SynchronousActionState& sema );
void SetEventType( CoreThreadStatus evt ) { m_evt = evt; }
CoreThreadStatus GetEventType() { return m_evt; }
protected:
void _DoInvoke();
};

View File

@ -42,12 +42,14 @@ void IEventListener_CoreThread::DispatchEvent( const CoreThreadStatus& status )
{
switch( status )
{
case CoreThread_Indeterminate: break;
case CoreThread_Started: CoreThread_OnStarted(); break;
case CoreThread_Resumed: CoreThread_OnResumed(); break;
case CoreThread_Suspended: CoreThread_OnSuspended(); break;
case CoreThread_Reset: CoreThread_OnReset(); break;
case CoreThread_Stopped: CoreThread_OnStopped(); break;
jNO_DEFAULT;
}
}
@ -74,7 +76,7 @@ void IEventListener_Plugins::DispatchEvent( const PluginEventType& pevt )
case CorePlugins_Closed: CorePlugins_OnClosed(); break;
case CorePlugins_Shutdown: CorePlugins_OnShutdown(); break;
case CorePlugins_Unloaded: CorePlugins_OnUnloaded(); break;
jNO_DEFAULT;
}
}
@ -102,3 +104,71 @@ void IEventListener_AppStatus::DispatchEvent( const AppEventInfo& evtinfo )
case AppStatus_Exiting: AppStatusEvent_OnExit(); break;
}
}
void Pcsx2App::DispatchEvent( PluginEventType evt )
{
if( !AffinityAssert_AllowFrom_MainUI() ) return;
m_evtsrc_CorePluginStatus.Dispatch( evt );
}
void Pcsx2App::DispatchEvent( AppEventType evt )
{
if( !AffinityAssert_AllowFrom_MainUI() ) return;
m_evtsrc_AppStatus.Dispatch( AppEventInfo( evt ) );
}
void Pcsx2App::DispatchEvent( CoreThreadStatus evt )
{
switch( evt )
{
case CoreThread_Started:
case CoreThread_Reset:
case CoreThread_Stopped:
FpsManager.Reset();
break;
case CoreThread_Resumed:
case CoreThread_Suspended:
FpsManager.Resume();
break;
}
// Clear the sticky key statuses, because hell knows what'll change while the PAD
// plugin is suspended.
m_kevt.m_shiftDown = false;
m_kevt.m_controlDown = false;
m_kevt.m_altDown = false;
m_evtsrc_CoreThreadStatus.Dispatch( evt );
ScopedBusyCursor::SetDefault( Cursor_NotBusy );
CoreThread.RethrowException();
}
void Pcsx2App::DispatchEvent( IniInterface& ini )
{
if( !AffinityAssert_AllowFrom_MainUI() ) return;
m_evtsrc_AppStatus.Dispatch( AppSettingsEventInfo( ini ) );
}
// --------------------------------------------------------------------------------------
// CoreThreadStatusEvent Implementations
// --------------------------------------------------------------------------------------
CoreThreadStatusEvent::CoreThreadStatusEvent( CoreThreadStatus evt, SynchronousActionState* sema )
: pxInvokeActionEvent( sema )
{
m_evt = evt;
}
CoreThreadStatusEvent::CoreThreadStatusEvent( CoreThreadStatus evt, SynchronousActionState& sema )
: pxInvokeActionEvent( sema )
{
m_evt = evt;
}
void CoreThreadStatusEvent::_DoInvoke()
{
sApp.DispatchEvent( m_evt );
}

View File

@ -19,9 +19,9 @@
//
// 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
// 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
// as either pointers or handles without running into complicated header file
// inter-dependence.
//
@ -30,7 +30,7 @@ class GSFrame;
class ConsoleLogFrame;
class PipeRedirectionBase;
class AppCoreThread;
class pxInvokeAppMethodEvent;
class Pcsx2AppMethodEvent;
class IniInterface;
// wxWidgets forward declarations

View File

@ -53,15 +53,6 @@ static void CpuCheckSSE2()
g_Conf->EmuOptions.Cpu.Recompiler.EnableVU1 = false;
}
void Pcsx2App::OpenWizardConsole()
{
if( !IsDebugBuild ) return;
g_Conf->ProgLogBox.Visible = true;
m_id_ProgramLogBox = (new ConsoleLogFrame( NULL, L"PCSX2 Program Log", g_Conf->ProgLogBox ))->GetId();
EnableAllLogging();
}
void Pcsx2App::WipeUserModeSettings()
{
wxDirName usrlocaldir( wxStandardPaths::Get().GetUserLocalDataDir() );
@ -116,14 +107,14 @@ void Pcsx2App::ReadUserModeSettings()
L"It will likely crash on all games, devour your young, and make you an object of shame and disgrace among your family and friends. "
L"Do not report any bugs with this version if you received this popup. \n\nYou have been warned. ", wxALIGN_CENTER
);
hackedVersion += new wxButton( &hackedVersion, wxID_OK ) | pxSizerFlags::StdCenter();
hackedVersion.ShowModal();
}
bool hasGroup = conf_usermode->HasGroup( groupname );
bool forceWiz = m_ForceWizard || !hasGroup;
if( !forceWiz )
{
conf_usermode->SetPath( groupname );
@ -141,18 +132,17 @@ void Pcsx2App::ReadUserModeSettings()
preAlpha.SetSizer( new wxBoxSizer( wxVERTICAL ) );
preAlpha += new pxStaticText( &preAlpha,
L"NOTICE!! This is a *PRE-ALPHA* developer build of PCSX2 0.9.7. We are in the middle of major rewrites of the "
L"NOTICE!! This is a *PRE-ALPHA* developer build of PCSX2 0.9.7. We are in the middle of major rewrites of the "
L"user interface, and many parts of the program have *NOT* been implemented yet. Options will be missing. "
L"Some things may crash or hang without warning. Other things will seem plainly stupid and the product of incompetent "
L"programmers. This is normal. We're working on it.\n\nYou have been warned!", wxALIGN_CENTER
);
preAlpha += new wxButton( &preAlpha, wxID_OK ) | pxSizerFlags::StdCenter();
preAlpha.ShowModal();
}
// first time startup, so give the user the choice of user mode:
OpenWizardConsole();
FirstTimeWizard wiz( NULL );
if( !wiz.RunWizard( wiz.GetUsermodePage() ) )
throw Exception::StartupAborted( L"Startup aborted: User canceled FirstTime Wizard." );
@ -178,7 +168,6 @@ void Pcsx2App::ReadUserModeSettings()
// user wiped their pcsx2.ini -- needs a reconfiguration via wizard!
// (we skip the first page since it's a usermode.ini thing)
OpenWizardConsole();
FirstTimeWizard wiz( NULL );
if( !wiz.RunWizard( wiz.GetPostUsermodePage() ) )
throw Exception::StartupAborted( L"Startup aborted: User canceled Configuration Wizard." );
@ -190,11 +179,10 @@ void Pcsx2App::ReadUserModeSettings()
AppSaveSettings();
}
}
// 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;
// force unload plugins loaded by the wizard. If we don't do this the recompilers might
// fail to allocate the memory they need to function.
UnloadPlugins();
}
void Pcsx2App::DetectCpuAndUserMode()
@ -211,9 +199,9 @@ void Pcsx2App::DetectCpuAndUserMode()
ReadUserModeSettings();
AppConfig_OnChangedSettingsFolder();
PostMethod( &Pcsx2App::OpenMainFrame );
PostMethod( &Pcsx2App::OpenConsoleLog );
PostMethod( &Pcsx2App::AllocateCoreStuffs );
PostAppMethod( &Pcsx2App::OpenMainFrame );
PostAppMethod( &Pcsx2App::OpenConsoleLog );
PostAppMethod( &Pcsx2App::AllocateCoreStuffs );
}
void Pcsx2App::OpenMainFrame()
@ -222,14 +210,13 @@ void Pcsx2App::OpenMainFrame()
MainEmuFrame* mainFrame = new MainEmuFrame( NULL, L"PCSX2" );
m_id_MainFrame = mainFrame->GetId();
mainFrame->PushEventHandler( &GetRecentIsoManager() );
if( wxWindow* deleteme = GetProgramLog() )
{
deleteme->Destroy();
g_Conf->ProgLogBox.Visible = true;
m_id_ProgramLogBox = wxID_ANY;
PostIdleMethod( &Pcsx2App::OpenConsoleLog );
PostIdleAppMethod( &Pcsx2App::OpenConsoleLog );
}
SetTopWindow( mainFrame ); // not really needed...
@ -259,7 +246,7 @@ void Pcsx2App::AllocateCoreStuffs()
// 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;
@ -273,7 +260,7 @@ void Pcsx2App::AllocateCoreStuffs()
);
exconf += scrollableTextArea | pxSizerFlags::StdExpand();
if( !m_CoreAllocs->IsRecAvailable_EE() )
{
scrollableTextArea->AppendText( L"* R5900 (EE)\n\n" );
@ -333,7 +320,7 @@ void Pcsx2App::AllocateCoreStuffs()
}
}
LoadPluginsPassive( NULL );
LoadPluginsPassive();
}
@ -414,15 +401,13 @@ bool Pcsx2App::OnCmdLineParsed( wxCmdLineParser& parser )
return true;
}
typedef void (wxEvtHandler::*pxInvokeMethodEventFunction)(pxInvokeAppMethodEvent&);
typedef void (wxEvtHandler::*pxInvokeAppMethodEventFunction)(Pcsx2AppMethodEvent&);
typedef void (wxEvtHandler::*pxStuckThreadEventHandler)(pxMessageBoxEvent&);
bool Pcsx2App::OnInit()
{
#define pxMethodEventHandler(func) \
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(pxInvokeMethodEventFunction, &func )
Connect( pxEvt_OpenModalDialog, wxCommandEventHandler( Pcsx2App::OnOpenModalDialog ) );
#define pxAppMethodEventHandler(func) \
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(pxInvokeAppMethodEventFunction, &func )
pxDoAssert = AppDoAssert;
@ -436,14 +421,7 @@ bool Pcsx2App::OnInit()
m_StderrRedirHandle = NewPipeRedir(stderr);
wxLocale::AddCatalogLookupPathPrefix( wxGetCwd() );
Connect( pxEvt_FreezeThreadFinished, wxCommandEventHandler (Pcsx2App::OnFreezeThreadFinished) );
Connect( pxEvt_CoreThreadStatus, wxCommandEventHandler (Pcsx2App::OnCoreThreadStatus) );
Connect( pxEvt_LoadPluginsComplete, wxCommandEventHandler (Pcsx2App::OnLoadPluginsComplete) );
Connect( pxEvt_PluginStatus, wxCommandEventHandler (Pcsx2App::OnPluginStatus) );
Connect( pxEvt_SysExecute, wxCommandEventHandler (Pcsx2App::OnSysExecute) );
Connect( pxEvt_InvokeMethod, pxMethodEventHandler (Pcsx2App::OnInvokeMethod) );
Connect( pxID_PadHandler_Keydown, wxEVT_KEY_DOWN, wxKeyEventHandler( Pcsx2App::OnEmuKeyDown ) );
Connect( pxID_PadHandler_Keydown, wxEVT_KEY_DOWN, wxKeyEventHandler (Pcsx2App::OnEmuKeyDown) );
// User/Admin Mode Dual Setup:
// PCSX2 now supports two fundamental modes of operation. The default is Classic mode,
@ -467,6 +445,7 @@ bool Pcsx2App::OnInit()
pxDwm_Load();
#endif
SysExecutorThread.Start();
DetectCpuAndUserMode();
}
// ----------------------------------------------------------------------------
@ -502,25 +481,17 @@ bool Pcsx2App::OnInit()
// OnExit() must use CleanupOnExit instead.
void Pcsx2App::CleanupRestartable()
{
AffinityAssert_AllowFromMain();
AffinityAssert_AllowFrom_MainUI();
// 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;
ShutdownPlugins();
SysExecutorThread.ShutdownQueue();
PingDispatcher( "Cleanup" );
DeletionDispatcher();
CoreThread.Cancel();
if( m_CorePlugins )
m_CorePlugins->Shutdown();
//PingDispatcher( "Cleanup" );
//DeletionDispatcher();
IdleEventDispatcher( "Cleanup" );
if( g_Conf )
AppSaveSettings();
sMainFrame.RemoveEventHandler( &GetRecentIsoManager() );
}
// This cleanup handler can be called from OnExit (it doesn't need a running message pump),
@ -529,14 +500,13 @@ void Pcsx2App::CleanupRestartable()
// to be friendly to the OnExit scenario (no message pump).
void Pcsx2App::CleanupOnExit()
{
AffinityAssert_AllowFromMain();
AffinityAssert_AllowFrom_MainUI();
try
{
CleanupRestartable();
CleanupResources();
}
catch( Exception::ThreadDeadlock& ) { throw; }
catch( Exception::CancelEvent& ) { throw; }
catch( Exception::RuntimeError& ex )
{
@ -551,13 +521,13 @@ void Pcsx2App::CleanupOnExit()
#ifdef __WXMSW__
pxDwm_Unload();
#endif
// Notice: deleting the plugin manager (unloading plugins) here causes Lilypad to crash,
// likely due to some pending message in the queue that references lilypad procs.
// We don't need to unload plugins anyway tho -- shutdown is plenty safe enough for
// closing out all the windows. So just leave it be and let the plugins get unloaded
// during the wxApp destructor. -- air
// FIXME: performing a wxYield() here may fix that problem. -- air
pxDoAssert = pxAssertImpl_LogIt;
@ -581,9 +551,30 @@ int Pcsx2App::OnExit()
return wxApp::OnExit();
}
Pcsx2App::Pcsx2App()
// --------------------------------------------------------------------------------------
// SysEventHandler
// --------------------------------------------------------------------------------------
class SysEvtHandler : public pxEvtHandler
{
public:
wxString GetEvtHandlerName() const { return L"SysExecutor"; }
protected:
// When the SysExec message queue is finally empty, we should check the state of
// the menus and make sure they're all consistent to the current emulation states.
void DoIdle()
{
UI_UpdateSysControls();
}
};
Pcsx2App::Pcsx2App()
: SysExecutorThread( new SysEvtHandler() )
{
m_PendingSaves = 0;
m_ScheduledTermination = false;
m_id_MainFrame = wxID_ANY;
m_id_GsFrame = wxID_ANY;
m_id_ProgramLogBox = wxID_ANY;

View File

@ -16,6 +16,8 @@
#include "PrecompiledHeader.h"
#include "IniInterface.h"
#include "MainFrame.h"
#include "GSFrame.h"
#include "Plugins.h"
#include "AppSaveStates.h"
#include "ps2/BiosTools.h"
@ -32,15 +34,10 @@
IMPLEMENT_APP(Pcsx2App)
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_InvokeMethod );
DEFINE_EVENT_TYPE( pxEvt_LogicalVsync );
DEFINE_EVENT_TYPE( pxEvt_OpenModalDialog );
DEFINE_EVENT_TYPE( pxEvt_ThreadTaskTimeout_SysExec );
DocsModeType DocsFolderMode = DocsFolder_User;
wxDirName SettingsFolder;
@ -50,6 +47,77 @@ bool UseDefaultSettingsFolder = true;
ScopedPtr<AppConfig> g_Conf;
ConfigOverrides OverrideOptions;
class NamedDialogBoxEvent : public BaseMessageBoxEvent
{
typedef BaseMessageBoxEvent _parent;
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(NamedDialogBoxEvent)
public:
virtual ~NamedDialogBoxEvent() throw() { }
virtual NamedDialogBoxEvent *Clone() const { return new NamedDialogBoxEvent(*this); }
NamedDialogBoxEvent() {}
NamedDialogBoxEvent( const wxString& name, SynchronousActionState& sync )
: BaseMessageBoxEvent( name, sync ) {}
NamedDialogBoxEvent( const wxString& name, SynchronousActionState* sync=NULL )
: BaseMessageBoxEvent( name, sync ) {}
protected:
int _DoDialog() const
{
const wxString& dlgName( m_Content );
if( dlgName.IsEmpty() ) return wxID_CANCEL;
if( wxWindow* window = wxFindWindowByName( dlgName ) )
{
if( wxDialog* dialog = wxDynamicCast( window, wxDialog ) )
{
window->SetFocus();
// It's legal to call ShowModal on a non-modal dialog, therefore making
// it modal in nature for the needs of whatever other thread of action wants
// to block against it:
if( !dialog->IsModal() )
{
int result = dialog->ShowModal();
dialog->Destroy();
return result;
}
}
}
else
{
using namespace Dialogs;
if( dlgName == SysConfigDialog::GetNameStatic() )
return SysConfigDialog().ShowModal();
if( dlgName == AppConfigDialog::GetNameStatic() )
return AppConfigDialog().ShowModal();
if( dlgName == BiosSelectorDialog::GetNameStatic() )
return BiosSelectorDialog().ShowModal();
if( dlgName == LogOptionsDialog::GetNameStatic() )
return LogOptionsDialog().ShowModal();
if( dlgName == AboutBoxDialog::GetNameStatic() )
return AboutBoxDialog().ShowModal();
}
return wxID_CANCEL;
}
};
IMPLEMENT_DYNAMIC_CLASS( NamedDialogBoxEvent, BaseMessageBoxEvent );
// 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
static int IssueDialogAsModal( const wxString& dlgName )
{
BaseMessageBoxEvent tevt( dlgName );
return Msgbox::ShowModal( tevt );
}
static bool HandlePluginError( Exception::PluginError& ex )
{
if( pxDialogExists( L"CoreSettings" ) ) return true;
@ -63,7 +131,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().IssueDialogAsModal( Dialogs::SysConfigDialog::GetNameStatic() ) == wxID_CANCEL )
if( IssueDialogAsModal( Dialogs::SysConfigDialog::GetNameStatic() ) == wxID_CANCEL )
return false;
}
return result;
@ -84,66 +152,56 @@ void Pcsx2App::PostMenuAction( MenuIdentifiers menu_id ) const
}
// --------------------------------------------------------------------------------------
// pxInvokeAppMethodEvent
// Pcsx2AppMethodEvent
// --------------------------------------------------------------------------------------
// Unlike pxPingEvent, the Semaphore belonging to this event is typically posted when the
// invoked method is completed. If the method can be executed in non-blocking fashion then
// it should leave the semaphore postback NULL.
//
class pxInvokeAppMethodEvent : public pxPingEvent
class Pcsx2AppMethodEvent : public pxInvokeActionEvent
{
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxInvokeAppMethodEvent)
typedef pxInvokeActionEvent _parent;
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(Pcsx2AppMethodEvent)
protected:
FnPtr_AppMethod m_Method;
FnPtr_Pcsx2App m_Method;
public:
virtual ~pxInvokeAppMethodEvent() throw() { }
virtual pxInvokeAppMethodEvent *Clone() const { return new pxInvokeAppMethodEvent(*this); }
virtual ~Pcsx2AppMethodEvent() throw() { }
virtual Pcsx2AppMethodEvent *Clone() const { return new Pcsx2AppMethodEvent(*this); }
explicit pxInvokeAppMethodEvent( int msgtype, FnPtr_AppMethod method=NULL, Semaphore* sema=NULL )
: pxPingEvent( msgtype, sema )
explicit Pcsx2AppMethodEvent( FnPtr_Pcsx2App method=NULL, SynchronousActionState* sema=NULL )
: pxInvokeActionEvent( sema )
{
m_Method = method;
}
explicit pxInvokeAppMethodEvent( FnPtr_AppMethod method=NULL, Semaphore* sema=NULL )
: pxPingEvent( pxEvt_InvokeMethod, sema )
explicit Pcsx2AppMethodEvent( FnPtr_Pcsx2App method, SynchronousActionState& sema )
: pxInvokeActionEvent( sema )
{
m_Method = method;
}
explicit pxInvokeAppMethodEvent( FnPtr_AppMethod method, Semaphore& sema )
: pxPingEvent( pxEvt_InvokeMethod, &sema )
{
m_Method = method;
}
pxInvokeAppMethodEvent( const pxInvokeAppMethodEvent& src )
: pxPingEvent( src )
Pcsx2AppMethodEvent( const Pcsx2AppMethodEvent& src )
: pxInvokeActionEvent( src )
{
m_Method = src.m_Method;
}
void Invoke() const
{
if( m_Method ) (wxGetApp().*m_Method)();
if( m_PostBack ) m_PostBack->Post();
}
void SetMethod( FnPtr_AppMethod method )
void SetMethod( FnPtr_Pcsx2App method )
{
m_Method = method;
}
protected:
void _DoInvoke()
{
if( m_Method ) (wxGetApp().*m_Method)();
}
};
IMPLEMENT_DYNAMIC_CLASS( pxInvokeAppMethodEvent, pxPingEvent )
void Pcsx2App::OnInvokeMethod( pxInvokeAppMethodEvent& evt )
{
evt.Invoke(); // wow this is easy!
}
IMPLEMENT_DYNAMIC_CLASS( Pcsx2AppMethodEvent, pxInvokeActionEvent )
#ifdef __WXGTK__
extern int TranslateGDKtoWXK( u32 keysym );
@ -206,7 +264,7 @@ void FramerateManager::Reset()
Resume();
}
//
//
void FramerateManager::Resume()
{
}
@ -218,7 +276,7 @@ void FramerateManager::DoFrame()
m_fpsqueue_writepos = (m_fpsqueue_writepos + 1) % FramerateQueueDepth;
m_fpsqueue[m_fpsqueue_writepos] = GetCPUTicks();
// intentionally leave 1 on the counter here, since ultimately we want to divide the
// intentionally leave 1 on the counter here, since ultimately we want to divide the
// final result (in GetFramerate() by QueueDepth-1.
if( m_initpause > 1 ) --m_initpause;
}
@ -232,13 +290,13 @@ double FramerateManager::GetFramerate() const
}
// LogicalVsync - Event received from the AppCoreThread (EEcore) for each vsync,
// roughly 50/60 times a second when frame limiting is enabled, and up to 10,000
// roughly 50/60 times a second when frame limiting is enabled, and up to 10,000
// times a second if not (ok, not quite, but you get the idea... I hope.)
void Pcsx2App::LogicalVsync()
{
if( PostMethodToMainThread( &Pcsx2App::LogicalVsync ) ) return;
if( PostAppMethodMyself( &Pcsx2App::LogicalVsync ) ) return;
if( !SysHasValidState() || g_plugins == NULL ) return;
if( !SysHasValidState() ) return;
// Update / Calculate framerate!
@ -249,78 +307,24 @@ void Pcsx2App::LogicalVsync()
if( (PADupdate != NULL) && (GSopen2 != NULL) && (wxGetApp().GetGsFramePtr() != NULL) )
PADupdate(0);
const keyEvent* ev = PADkeyEvent();
if( (ev != NULL) && (ev->key != 0) )
while( const keyEvent* ev = PADkeyEvent() )
{
if( ev->key == 0 ) break;
// Give plugins first try to handle keys. If none of them handles the key, it will
// be passed to the main user interface.
if( !g_plugins->KeyEvent( *ev ) )
if( !GetCorePlugins().KeyEvent( *ev ) )
PadKeyDispatch( *ev );
}
}
// ----------------------------------------------------------------------------
// Pcsx2App Event Handlers
// ----------------------------------------------------------------------------
// Invoked by the AppCoreThread when it's internal status has changed.
// evt.GetInt() reflects the status at the time the message was sent, which may differ
// from the actual status. Typically listeners bound to this will want to use direct
// polling of the CoreThread rather than the belated status.
void Pcsx2App::OnCoreThreadStatus( wxCommandEvent& evt )
{
CoreThreadStatus status = (CoreThreadStatus)evt.GetInt();
switch( status )
{
case CoreThread_Started:
case CoreThread_Reset:
case CoreThread_Stopped:
FpsManager.Reset();
break;
case CoreThread_Resumed:
case CoreThread_Suspended:
FpsManager.Resume();
break;
}
// Clear the sticky key statuses, because hell knows what'll change while the PAD
// plugin is suspended.
m_kevt.m_shiftDown = false;
m_kevt.m_controlDown = false;
m_kevt.m_altDown = false;
m_evtsrc_CoreThreadStatus.Dispatch( status );
ScopedBusyCursor::SetDefault( Cursor_NotBusy );
CoreThread.RethrowException();
}
void Pcsx2App::OnOpenModalDialog( wxCommandEvent& evt )
{
pxAssertDev( !evt.GetString().IsEmpty(), wxNullChar );
MsgboxEventResult* evtres = (MsgboxEventResult*)evt.GetClientData();
wxWindowID result = IssueDialogAsModal( evt.GetString() );
if( evtres != NULL )
{
evtres->result = result;
evtres->WaitForMe.Post();
}
}
void Pcsx2App::OnOpenDialog_StuckThread( wxCommandEvent& evt )
{
if( !pxAssert( evt.GetClientData() != NULL ) ) return;
DoStuckThread( *(PersistentThread*)evt.GetClientData() );
}
int Pcsx2App::DoStuckThread( PersistentThread& stuck_thread )
/*int Pcsx2App::DoStuckThread( PersistentThread& stuck_thread )
{
if( !wxThread::IsMain() )
{
@ -336,59 +340,8 @@ int Pcsx2App::DoStuckThread( PersistentThread& stuck_thread )
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;
if( !wxThread::IsMain() )
{
MsgboxEventResult result;
PostCommand( &result, pxEvt_OpenModalDialog, 0, 0, dlgName );
result.WaitForMe.WaitNoCancel();
return result.result;
}
if( wxWindow* window = wxFindWindowByName( dlgName ) )
{
if( wxDialog* dialog = wxDynamicCast( window, wxDialog ) )
{
window->SetFocus();
// It's legal to call ShowModal on a non-modal dialog, therefore making
// it modal in nature for the needs of whatever other thread of action wants
// to block against it:
if( !dialog->IsModal() )
{
int result = dialog->ShowModal();
dialog->Destroy();
return result;
}
}
}
else
{
using namespace Dialogs;
if( dlgName == SysConfigDialog::GetNameStatic() )
return SysConfigDialog().ShowModal();
if( dlgName == AppConfigDialog::GetNameStatic() )
return AppConfigDialog().ShowModal();
if( dlgName == BiosSelectorDialog::GetNameStatic() )
return BiosSelectorDialog().ShowModal();
if( dlgName == LogOptionsDialog::GetNameStatic() )
return LogOptionsDialog().ShowModal();
if( dlgName == AboutBoxDialog::GetNameStatic() )
return AboutBoxDialog().ShowModal();
}
return wxID_CANCEL;
}
HashTools::HashMap<int, const GlobalCommandDescriptor*> GlobalAccels( 0, 0xffffffff );
@ -446,7 +399,7 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
wxDialogWithHelpers dialog( NULL, _("PS2 BIOS Error"), wxVERTICAL );
dialog += dialog.Heading( ex.FormatDisplayMessage() + BIOS_GetMsg_Required() + _("\nPress Ok to go to the BIOS Configuration Panel.") );
dialog += new ModalButtonPanel( &dialog, MsgButtons().OKCancel() );
if( dialog.ShowModal() == wxID_CANCEL )
Console.Warning( "User denied option to re-configure BIOS." );
@ -466,7 +419,7 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
}
catch( Exception::PluginInitError& ex )
{
if( m_CorePlugins ) m_CorePlugins->Shutdown();
CorePlugins.Shutdown();
Console.Error( ex.FormatDiagnosticMessage() );
if( !HandlePluginError( ex ) )
@ -477,7 +430,7 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
}
catch( Exception::PluginError& ex )
{
if( m_CorePlugins ) m_CorePlugins->Close();
CorePlugins.Close();
Console.Error( ex.FormatDiagnosticMessage() );
if( !HandlePluginError( ex ) )
@ -487,15 +440,16 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
}
}
// ----------------------------------------------------------------------------
#if 0
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
// a little tired, so maybe later!) --air
Console.Warning( ex.FormatDiagnosticMessage() );
wxDialogWithHelpers dialog( NULL, _("PCSX2 Unresponsive Thread"), wxVERTICAL );
dialog += dialog.Heading( ex.FormatDisplayMessage() + L"\n\n" +
pxE( ".Popup Error:Thread Deadlock Actions",
L"'Ignore' to continue waiting for the thread to respond.\n"
@ -505,7 +459,7 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
);
int result = pxIssueConfirmation( dialog, MsgButtons().Ignore().Cancel().Custom( _("Terminate") ) );
if( result == pxID_CUSTOM )
{
// fastest way to kill the process! (works in Linux and win32, thanks to windows having very
@ -522,6 +476,7 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
// Ignore does nothing...
}
#endif
// ----------------------------------------------------------------------------
catch( Exception::CancelEvent& ex )
{
@ -537,6 +492,27 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
}
}
void Pcsx2App::StartPendingSave()
{
if( PostAppMethodMyself(&Pcsx2App::StartPendingSave) ) return;
++m_PendingSaves;
}
void Pcsx2App::ClearPendingSave()
{
if( PostAppMethodMyself(&Pcsx2App::StartPendingSave) ) return;
--m_PendingSaves;
pxAssumeDev( m_PendingSaves >= 0, "Pending saves count mismatch (pending count is less than 0)" );
if( (m_PendingSaves == 0) && m_ScheduledTermination )
{
Console.WriteLn( "App: All pending saves completed; exiting!" );
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
// to handle cancelable window closures)
@ -545,10 +521,18 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
// the glorious user, whomever (s)he-it might be.
void Pcsx2App::PrepForExit()
{
CancelLoadingPlugins();
SysExecutorThread.ShutdownQueue();
//SysExecutorThread.Cancel();
DispatchEvent( AppStatus_Exiting );
if( m_PendingSaves != 0 )
{
Console.WriteLn( "App: Saves are pending; exit postponed..." );
sApp.SetExitOnFrameDelete( false );
return;
}
// 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).
@ -577,10 +561,11 @@ GSFrame& Pcsx2App::GetGsFrame() const
return *gsFrame;
}
void AppApplySettings( const AppConfig* oldconf )
{
AffinityAssert_AllowFromMain();
AffinityAssert_AllowFrom_MainUI();
ScopedCoreThreadClose suspend_core;
g_Conf->Folders.ApplyDefaults();
@ -593,18 +578,8 @@ void AppApplySettings( const AppConfig* oldconf )
g_Conf->EmuOptions.BiosFilename = g_Conf->FullpathToBios();
ScopedCoreThreadSuspend suspend_core;
if( g_plugins != NULL )
g_plugins->SetSettingsFolder( GetSettingsFolder().ToString() );
RelocateLogfile();
// Update the compression attribute on the Memcards folder.
// Memcards generally compress very well via NTFS compression.
NTFS_CompressFile( g_Conf->Folders.MemoryCards.ToString(), g_Conf->McdEnableNTFS );
if( (oldconf == NULL) || (oldconf->LanguageId != g_Conf->LanguageId) )
{
wxDoNotLogInThisScope please;
@ -616,11 +591,27 @@ void AppApplySettings( const AppConfig* oldconf )
}
}
}
CorePlugins.SetSettingsFolder( GetSettingsFolder().ToString() );
// Update the compression attribute on the Memcards folder.
// Memcards generally compress very well via NTFS compression.
NTFS_CompressFile( g_Conf->Folders.MemoryCards.ToString(), g_Conf->McdEnableNTFS );
sApp.DispatchEvent( AppStatus_SettingsApplied );
suspend_core.Resume();
suspend_core.AllowResume();
}
// --------------------------------------------------------------------------------------
// pxDudConfig
// --------------------------------------------------------------------------------------
// Used to handle config actions prior to the creation of the ini file (for example, the
// first time wizard). Attempts to save ini settings are simply ignored through this
// class, which allows us to give the user a way to set everything up in the wizard, apply
// settings as usual, and only *save* something once the whole wizard is complete.
//
class pxDudConfig : public wxConfigBase
{
protected:
@ -691,16 +682,25 @@ AppIniLoader::AppIniLoader()
void AppLoadSettings()
{
if( !AffinityAssert_AllowFromMain() ) return;
if( wxGetApp().PostMethodMyself(AppLoadSettings) ) return;
AppIniLoader loader;
g_Conf->LoadSave( loader );
if( !wxFile::Exists( g_Conf->CurrentIso ) )
g_Conf->CurrentIso.Clear();
sApp.DispatchEvent( loader );
}
void AppSaveSettings()
{
if( !AffinityAssert_AllowFromMain() ) return;
if( wxGetApp().PostMethodMyself(AppSaveSettings) ) return;
if( !wxFile::Exists( g_Conf->CurrentIso ) )
g_Conf->CurrentIso.Clear();
sApp.GetRecentIsoManager().Add( g_Conf->CurrentIso );
AppIniSaver saver;
g_Conf->LoadSave( saver );
@ -709,7 +709,7 @@ void AppSaveSettings()
// 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.
// PostAppMethodMyself.
//
// This function works something like setjmp/longjmp, in that the return value indicates if the
// function actually executed the specified method or not.
@ -718,21 +718,20 @@ 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::InvokeMethodOnMainThread( FnPtr_AppMethod method )
bool Pcsx2App::InvokeOnMainThread( FnPtr_Pcsx2App method )
{
if( wxThread::IsMain() ) return false;
Semaphore sem;
pxInvokeAppMethodEvent evt( method, sem );
AddPendingEvent( evt );
sem.Wait();
SynchronousActionState sync;
PostEvent( Pcsx2AppMethodEvent( method, sync ) );
sync.WaitForResult();
return true;
}
// 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.
// InvokeOnMainThread.
//
// This function works something like setjmp/longjmp, in that the return value indicates if the
// function actually executed the specified method or not.
@ -741,33 +740,31 @@ bool Pcsx2App::InvokeMethodOnMainThread( FnPtr_AppMethod method )
// 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 )
bool Pcsx2App::PostAppMethodMyself( FnPtr_Pcsx2App method )
{
if( wxThread::IsMain() ) return false;
pxInvokeAppMethodEvent evt( method );
AddPendingEvent( evt );
PostEvent( Pcsx2AppMethodEvent( 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 )
void Pcsx2App::PostAppMethod( FnPtr_Pcsx2App method )
{
pxInvokeAppMethodEvent evt( method );
AddPendingEvent( evt );
PostEvent( Pcsx2AppMethodEvent( method ) );
}
// Posts a method to the main thread; non-blocking. Post occurs even when called from the
// main thread.
void Pcsx2App::PostIdleMethod( FnPtr_AppMethod method )
void Pcsx2App::PostIdleAppMethod( FnPtr_Pcsx2App method )
{
pxInvokeAppMethodEvent evt( method );
OnAddEventToIdleQueue( evt );
Pcsx2AppMethodEvent evt( method );
AddIdleEvent( evt );
}
void Pcsx2App::OpenGsPanel()
{
if( InvokeMethodOnMainThread( &Pcsx2App::OpenGsPanel ) ) return;
if( InvokeOnMainThread( &Pcsx2App::OpenGsPanel ) ) return;
GSFrame* gsFrame = GetGsFramePtr();
if( gsFrame == NULL )
@ -786,7 +783,7 @@ void Pcsx2App::OpenGsPanel()
// Doing an immediate hide/show didn't work. So now I'm trying a resize. Because
// wxWidgets is "clever" (grr!) it optimizes out just force-setting the same size
// over again, so instead I resize it to size-1 and then back to the original size.
const wxSize oldsize( gsFrame->GetSize() );
wxSize newsize( oldsize );
newsize.DecBy(1);
@ -794,8 +791,8 @@ void Pcsx2App::OpenGsPanel()
gsFrame->SetSize( newsize );
gsFrame->SetSize( oldsize );
}
pxAssumeDev( !GetPluginManager().IsOpen( PluginId_GS ), "GS Plugin must be closed prior to opening a new Gs Panel!" );
pxAssumeDev( !GetCorePlugins().IsOpen( PluginId_GS ), "GS Plugin must be closed prior to opening a new Gs Panel!" );
gsFrame->Show();
pDsp = (uptr)gsFrame->GetViewport()->GetHandle();
@ -806,7 +803,7 @@ void Pcsx2App::OpenGsPanel()
void Pcsx2App::CloseGsPanel()
{
if( InvokeMethodOnMainThread( &Pcsx2App::CloseGsPanel ) ) return;
if( InvokeOnMainThread( &Pcsx2App::CloseGsPanel ) ) return;
GSFrame* gsFrame = GetGsFramePtr();
if( (gsFrame != NULL) && CloseViewportWithPlugins )
@ -838,45 +835,77 @@ void Pcsx2App::OnMainFrameClosed( wxWindowID id )
}
// --------------------------------------------------------------------------------------
// Sys/Core API and Shortcuts (for wxGetApp())
// SysExecuteEvent
// --------------------------------------------------------------------------------------
static int _sysexec_cdvdsrc_type = -1;
static wxString _sysexec_elf_override;
static void _sendmsg_SysExecute()
class SysExecuteEvent : public SysExecEvent
{
if( !CoreThread.AcquireResumeLock() )
protected:
bool m_UseCDVDsrc;
CDVD_SourceType m_cdvdsrc_type;
wxString m_elf_override;
public:
virtual ~SysExecuteEvent() throw() {}
SysExecuteEvent* Clone() const { return new SysExecuteEvent(*this); }
wxString GetEventName() const
{
DbgCon.WriteLn( "(SysExecute) another resume lock or message is already pending; no message posted." );
return;
return L"SysExecute";
}
AppSaveSettings();
wxGetApp().PostCommand( pxEvt_SysExecute, _sysexec_cdvdsrc_type );
}
wxString GetEventMessage() const
{
return _("Executing PS2 Virtual Machine...");
}
SysExecuteEvent()
{
m_UseCDVDsrc = false;
}
static void OnSysExecuteAfterPlugins( const wxCommandEvent& loadevt )
{
if( (wxTheApp == NULL) || !((Pcsx2App*)wxTheApp)->m_CorePlugins ) return;
_sendmsg_SysExecute();
}
SysExecuteEvent( const wxString& elf_override )
: m_elf_override( elf_override )
{
m_UseCDVDsrc = false;
}
SysExecuteEvent( CDVD_SourceType srctype, const wxString& elf_override )
: m_elf_override( elf_override )
{
m_cdvdsrc_type = srctype;
m_UseCDVDsrc = true;
}
protected:
void _DoInvoke()
{
wxGetApp().ProcessMethod( AppSaveSettings );
// if something unloaded plugins since this messages was queued then it's best to ignore
// it, because apparently too much stuff is going on and the emulation states are wonky.
if( !CorePlugins.AreLoaded() ) return;
DbgCon.WriteLn( Color_Gray, "(SysExecute) received." );
if( m_UseCDVDsrc ) CoreThread.Shutdown(); else CoreThread.Suspend();
CDVDsys_SetFile( CDVDsrc_Iso, g_Conf->CurrentIso );
if( m_UseCDVDsrc )
CDVDsys_ChangeSource( m_cdvdsrc_type );
else if( CDVD == NULL )
CDVDsys_ChangeSource( CDVDsrc_NoDisc );
if( !CoreThread.HasValidState() )
CoreThread.SetElfOverride( m_elf_override );
CoreThread.Resume();
}
};
// Executes the emulator using a saved/existing virtual machine state and currently
// configured CDVD source device.
void Pcsx2App::SysExecute()
{
_sysexec_cdvdsrc_type = -1;
_sysexec_elf_override = CoreThread.GetElfOverride();
if( !m_CorePlugins )
{
LoadPluginsPassive( OnSysExecuteAfterPlugins );
return;
}
DbgCon.WriteLn( Color_Gray, "(SysExecute) Queuing request to re-execute existing VM state." );
_sendmsg_SysExecute();
SysExecutorThread.PostEvent( new SysExecuteEvent(CoreThread.GetElfOverride()) );
}
// Executes the specified cdvd source and optional elf file. This command performs a
@ -884,59 +913,38 @@ void Pcsx2App::SysExecute()
// sources.
void Pcsx2App::SysExecute( CDVD_SourceType cdvdsrc, const wxString& elf_override )
{
_sysexec_cdvdsrc_type = (int)cdvdsrc;
_sysexec_elf_override = elf_override;
if( !m_CorePlugins )
{
LoadPluginsPassive( OnSysExecuteAfterPlugins );
return;
}
DbgCon.WriteLn( Color_Gray, "(SysExecute) Queuing request for new VM state." );
_sendmsg_SysExecute();
SysExecutorThread.PostEvent( new SysExecuteEvent(cdvdsrc, CoreThread.GetElfOverride()) );
}
// This message performs actual system execution (as dictated by SysExecute variants).
// It is implemented as a message handler so that it can be triggered in response to
// the completion of other dependent activities, namely loading plugins.
void Pcsx2App::OnSysExecute( wxCommandEvent& evt )
// --------------------------------------------------------------------------------------
// SysExecEvent_Shutdown
// --------------------------------------------------------------------------------------
class SysExecEvent_Shutdown : public SysExecEvent
{
CoreThread.ReleaseResumeLock();
if( sys_resume_lock > 0 )
public:
wxString GetEventName() const
{
Console.WriteLn( "SysExecute: State is locked, ignoring Execute request!" );
return;
return L"SysShutdown";
}
wxString GetEventMessage() const
{
return _("Resetting PS2 virtual machine...");
}
// if something unloaded plugins since this messages was queued then it's best to ignore
// it, because apparently too much stuff is going on and the emulation states are wonky.
if( !m_CorePlugins ) return;
protected:
void _DoInvoke()
{
StateCopy_Clear();
CoreThread.Shutdown();
}
DbgCon.WriteLn( Color_Gray, "(MainThread) SysExecute received." );
if( evt.GetInt() != -1 ) CoreThread.Reset(); else CoreThread.Suspend();
CDVDsys_SetFile( CDVDsrc_Iso, g_Conf->CurrentIso );
if( evt.GetInt() != -1 )
CDVDsys_ChangeSource( (CDVD_SourceType)evt.GetInt() );
else if( CDVD == NULL )
CDVDsys_ChangeSource( CDVDsrc_NoDisc );
if( !CoreThread.HasValidState() )
CoreThread.SetElfOverride( _sysexec_elf_override );
CoreThread.Resume();
}
};
// Full system reset stops the core thread and unloads all core plugins *completely*.
void Pcsx2App::SysReset()
void Pcsx2App::SysShutdown()
{
StateCopy_Clear();
CoreThread.Reset();
CoreThread.Cancel();
CoreThread.ReleaseResumeLock();
m_CorePlugins = NULL;
SysExecutorThread.PostEvent( new SysExecEvent_Shutdown() );
}
// Returns true if there is a "valid" virtual machine state from the user's perspective. This
@ -1002,39 +1010,3 @@ 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

@ -15,63 +15,39 @@
#pragma once
#include "App.h"
#include "SaveState.h"
enum SaveStateActionType
{
SaveStateAction_CreateFinished,
SaveStateAction_RestoreFinished,
SaveStateAction_ZipToDiskFinished,
SaveStateAction_UnzipFromDiskFinished,
};
// --------------------------------------------------------------------------------------
// IEventListener_SaveStateThread
// SaveSinglePluginHelper
// --------------------------------------------------------------------------------------
class IEventListener_SaveStateThread : public IEventDispatcher<SaveStateActionType>
// A scoped convenience class for closing a single plugin and saving its state to memory.
// Emulation is suspended as needed, and is restored when the object leaves scope. Within
// the scope of the object, code is free to call plugin re-configurations or even unload
// a plugin entirely and re-load a different plugin in its place.
//
class SaveSinglePluginHelper
{
public:
typedef SaveStateActionType EvtParams;
public:
IEventListener_SaveStateThread() {}
virtual ~IEventListener_SaveStateThread() throw() {}
virtual void DispatchEvent( const SaveStateActionType& status )
{
switch( status )
{
case SaveStateAction_CreateFinished: SaveStateAction_OnCreateFinished(); break;
case SaveStateAction_RestoreFinished: SaveStateAction_OnRestoreFinished(); break;
case SaveStateAction_ZipToDiskFinished: SaveStateAction_OnZipToDiskFinished(); break;
case SaveStateAction_UnzipFromDiskFinished: SaveStateAction_OnUnzipFromDiskFinished(); break;
jNO_DEFAULT;
}
}
protected:
virtual void SaveStateAction_OnCreateFinished() {}
virtual void SaveStateAction_OnRestoreFinished() {}
virtual void SaveStateAction_OnZipToDiskFinished() {}
virtual void SaveStateAction_OnUnzipFromDiskFinished() {}
SafeArray<u8> m_plugstore;
bool m_validstate;
PluginsEnum_t m_pid;
ScopedCoreThreadPause m_scoped_pause;
public:
SaveSinglePluginHelper( PluginsEnum_t pid );
virtual ~SaveSinglePluginHelper() throw();
};
extern bool StateCopy_InvokeOnSaveComplete( IActionInvocation* sst );
extern bool StateCopy_InvokeOnCopyComplete( IActionInvocation* sst );
extern SafeArray<u8>& StateCopy_GetBuffer();
extern bool StateCopy_IsValid();
extern void StateCopy_FreezeToMem();
extern void StateCopy_FreezeToMem_Blocking();
extern void StateCopy_ThawFromMem_Blocking();
extern void StateCopy_SaveToFile( const wxString& file );
extern void StateCopy_LoadFromFile( const wxString& file );
extern void StateCopy_SaveToSlot( uint num );
extern void StateCopy_LoadFromSlot( uint slot );
extern void StateCopy_Clear();
extern bool StateCopy_IsBusy();
extern const SafeArray<u8>* StateCopy_GetBuffer();

View File

@ -179,5 +179,7 @@ public:
);
virtual ~ApplicableWizardPage() throw() { m_ApplyState.DoCleanup(); }
virtual bool PrepForApply();
};

View File

@ -101,7 +101,7 @@ protected:
int m_win32_LinesPerPage;
int m_win32_LinesPerScroll;
#endif
bool m_IsPaused;
ScopedPtr<ScopedCoreThreadPause> m_IsPaused;
bool m_FreezeWrites;
public:
@ -217,7 +217,7 @@ protected:
// Lock object for accessing or modifying the following three vars:
// m_QueueBuffer, m_QueueColorSelection, m_CurQueuePos
MutexLockRecursive m_QueueLock;
MutexRecursive m_QueueLock;
// Describes a series of colored text sections in the m_QueueBuffer.
SafeList<ColorSection> m_QueueColorSection;

View File

@ -32,6 +32,15 @@ ApplicableWizardPage::ApplicableWizardPage( wxWizard* parent, wxWizardPage* prev
{
}
// This is a hack feature substitute for prioritized apply events. This callback is issued prior
// to the apply chain being run, allowing a panel to do some pre-apply prepwork. PAnels implementing
// this function should not modify the g_conf state.
bool ApplicableWizardPage::PrepForApply()
{
return true;
}
// ----------------------------------------------------------------------------
Panels::SettingsDirPickerPanel::SettingsDirPickerPanel( wxWindow* parent ) :
DirPickerPanel( parent, FolderId_Settings, _("Settings"), _("Select a folder for PCSX2 settings") )
@ -63,7 +72,7 @@ FirstTimeWizard::UsermodePage::UsermodePage( wxWizard* parent ) :
m_dirpick_settings = new SettingsDirPickerPanel( &panel );
m_panel_LangSel = new LanguageSelectionPanel( &panel );
m_panel_UserSel = new UsermodeSelectionPanel( &panel );
m_panel_UserSel = new DocsFolderPickerPanel( &panel );
panel += panel.Heading(_("PCSX2 is starting from a new or unknown folder and needs to be configured."));
@ -93,6 +102,37 @@ void FirstTimeWizard::UsermodePage::OnCustomDirChanged( wxCommandEvent& evt )
OnUsermodeChanged( evt );
}
bool FirstTimeWizard::UsermodePage::PrepForApply()
{
wxDirName path( PathDefs::GetDocuments(m_panel_UserSel->GetDocsMode()) );
if( path.FileExists() )
{
// FIXME: There's already a file by the same name.. not sure what we should do here.
throw Exception::BadStream( path.ToString(),
L"Targeted documents folder is already occupied by a file.",
pxE( "Error:DocsFolderFileConflict",
L"PCSX2 cannot create a documents folder in the requested location. "
L"The path name matches an existing file. Delete the file or change the documents location, "
L"and then try again."
)
);
}
if( !path.Exists() )
{
wxDialogWithHelpers dialog( NULL, _("Create folder?"), wxVERTICAL );
dialog += dialog.Heading( _("PCSX2 will create the following folder for documents. You can change this setting later, at any time.") );
dialog += 12;
dialog += dialog.Heading( path.ToString() );
if( wxID_CANCEL == pxIssueConfirmation( dialog, MsgButtons().Custom(_("Create")).Cancel(), L"CreateNewFolder" ) )
return false;
}
path.Mkdir();
return true;
}
// ----------------------------------------------------------------------------
FirstTimeWizard::FirstTimeWizard( wxWindow* parent )
: wxWizard( parent, wxID_ANY, _("PCSX2 First Time Configuration") )
@ -141,6 +181,18 @@ FirstTimeWizard::~FirstTimeWizard() throw()
}
static void _OpenConsole()
{
g_Conf->ProgLogBox.Visible = true;
wxGetApp().OpenConsoleLog();
}
int FirstTimeWizard::ShowModal()
{
if( IsDebugBuild ) wxGetApp().PostIdleMethod( _OpenConsole );
return _parent::ShowModal();
}
void FirstTimeWizard::OnDoubleClicked( wxCommandEvent& evt )
{
wxWindow* forwardButton = FindWindow( wxID_FORWARD );
@ -166,7 +218,7 @@ void FirstTimeWizard::OnPageChanging( wxWizardEvent& evt )
{
if( ApplicableWizardPage* page = wxDynamicCast( GetCurrentPage(), ApplicableWizardPage ) )
{
if( !page->GetApplyState().ApplyAll() )
if( !page->PrepForApply() || !page->GetApplyState().ApplyAll() )
{
evt.Veto();
return;

View File

@ -26,16 +26,20 @@ static const wxWindowID pxID_CUSTOM = wxID_LOWEST - 1;
class FirstTimeWizard : public wxWizard
{
typedef wxWizard _parent;
protected:
class UsermodePage : public ApplicableWizardPage
{
protected:
Panels::DirPickerPanel* m_dirpick_settings;
Panels::LanguageSelectionPanel* m_panel_LangSel;
Panels::UsermodeSelectionPanel* m_panel_UserSel;
Panels::DocsFolderPickerPanel* m_panel_UserSel;
public:
UsermodePage( wxWizard* parent );
virtual ~UsermodePage() throw() { }
bool PrepForApply();
protected:
void OnUsermodeChanged( wxCommandEvent& evt );
@ -61,6 +65,8 @@ public:
{
m_panel_PluginSel.OnShown();
}
int ShowModal();
protected:
virtual void OnPageChanging( wxWizardEvent& evt );
@ -88,7 +94,7 @@ namespace Dialogs
class PickUserModeDialog : public BaseApplicableDialog
{
protected:
Panels::UsermodeSelectionPanel* m_panel_usersel;
Panels::DocsFolderPickerPanel* m_panel_usersel;
Panels::LanguageSelectionPanel* m_panel_langsel;
public:

View File

@ -23,7 +23,7 @@ using namespace Panels;
Dialogs::PickUserModeDialog::PickUserModeDialog( wxWindow* parent )
: BaseApplicableDialog( parent, _("PCSX2 First Time configuration"), wxVERTICAL )
{
m_panel_usersel = new UsermodeSelectionPanel( this, false );
m_panel_usersel = new DocsFolderPickerPanel( this, false );
m_panel_langsel = new LanguageSelectionPanel( this );
*this += new pxStaticHeading( this, _("PCSX2 is starting from a new or unknown folder and needs to be configured.") );

View File

@ -0,0 +1,391 @@
/* 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"
// This is called from InvokeAction after various affinity and state checks have verified the
// message as executable. Override this when possible. Only override InvokeAction if you
// need some kind of additional low-level ability.
void SysExecEvent::_DoInvoke()
{
}
void SysExecEvent::SetException( BaseException* ex )
{
if( !ex ) return;
const wxString& prefix( wxsFormat(L"(%s) ", GetEventName()) );
ex->DiagMsg() = prefix + ex->DiagMsg();
//ex->UserMsg() = prefix + ex->UserMsg();
if( m_sync )
{
// Throws the exception inline with the message handler (this makes the exception
// look like it was thrown quite naturally).
m_sync->SetException( ex );
}
else
{
// transport the exception to the main thread, since the message is fully
// asynchronous. Message is sent as a non-blocking action since proper handling
// of user errors on async messages is *usually* to log/ignore it (hah), or to
// suspend emulation and issue a dialog box to the user.
wxGetApp().PostEvent( pxExceptionEvent( ex ) );
}
}
void SysExecEvent::SetException( const BaseException& ex )
{
SetException( ex.Clone() );
}
// This method calls _DoInvoke after performing some setup and affinity checks.
// Override _DoInvoke instead, which is the intended method of implementing derived class invocation.
void SysExecEvent::InvokeAction()
{
//pxAssumeDev( !IsBeingDeleted(), "Attempted to process a deleted SysExecutor event." );
AffinityAssert_AllowFrom_SysExecutor();
try {
_DoInvoke();
}
catch( BaseException& ex )
{
SetException( ex );
}
catch( std::runtime_error& ex )
{
SetException( new Exception::RuntimeError(ex) );
}
PostResult();
}
void SysExecEvent::PostResult() const
{
if( m_sync ) m_sync->PostResult();
}
pxEvtHandler::pxEvtHandler()
{
AtomicExchange( m_Quitting, false );
}
void pxEvtHandler::ShutdownQueue()
{
if( m_Quitting ) return;
AtomicExchange( m_Quitting, true );
m_wakeup.Post();
}
struct ScopedThreadCancelDisable
{
ScopedThreadCancelDisable()
{
int oldstate;
pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate );
}
~ScopedThreadCancelDisable() throw()
{
int oldstate;
pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldstate );
}
};
void pxEvtHandler::ProcessPendingEvents()
{
ScopedLock synclock( m_mtx_pending );
pxEvtList::iterator node;
while( node = m_pendingEvents.begin(), node != m_pendingEvents.end() )
{
ScopedPtr<SysExecEvent> deleteMe(wx_static_cast(SysExecEvent *, *node));
m_pendingEvents.erase( node );
if( !m_Quitting || deleteMe->IsCriticalEvent() )
{
// Some messages can be blocking, so we should release the mutex lock
// to avoid having cases where the main thread deadlocks simply trying
// to add a message to the queue.
synclock.Release();
if( deleteMe->AllowCancelOnExit() )
deleteMe->InvokeAction();
else
{
ScopedThreadCancelDisable thr_cancel_scope;
deleteMe->InvokeAction();
}
synclock.Acquire();
}
else
{
Console.WriteLn( L"(pxEvtHandler:Skipping Event) %s", deleteMe->GetEventName().c_str() );
deleteMe->PostResult();
}
}
}
// This method is provided for wxWidgets API conformance.
void pxEvtHandler::AddPendingEvent( SysExecEvent& evt )
{
PostEvent( evt );
}
void pxEvtHandler::PostEvent( SysExecEvent* evt )
{
if( !evt ) return;
if( m_Quitting )
{
evt->PostResult();
return;
}
ScopedLock synclock( m_mtx_pending );
DbgCon.WriteLn( L"(%s) Posting event: %s (queue count=%d)", GetEventHandlerName().c_str(), evt->GetEventName().c_str(), m_pendingEvents.size() );
m_pendingEvents.push_back( evt );
if( m_pendingEvents.size() == 1)
m_wakeup.Post();
}
void pxEvtHandler::PostEvent( const SysExecEvent& evt )
{
if( m_Quitting )
{
evt.PostResult();
return;
}
ScopedLock synclock( m_mtx_pending );
m_pendingEvents.push_back( evt.Clone() );
if( m_pendingEvents.size() == 1)
m_wakeup.Post();
}
void pxEvtHandler::ProcessEvent( SysExecEvent& evt )
{
if( wxThread::GetCurrentId() != m_OwnerThreadId )
{
SynchronousActionState sync;
evt.SetSyncState( sync );
PostEvent( evt );
sync.WaitForResult();
}
else
evt.InvokeAction();
}
bool pxEvtHandler::SelfProcessMethod( FnType_Void* method )
{
if( wxThread::GetCurrentId() != m_OwnerThreadId )
{
SynchronousActionState sync;
SysExecEvent_Method evt(method);
evt.SetSyncState( sync );
PostEvent( evt );
sync.WaitForResult();
return true;
}
return false;
}
void pxEvtHandler::ProcessEvent( SysExecEvent* evt )
{
if( !evt ) return;
if( wxThread::GetCurrentId() != m_OwnerThreadId )
{
SynchronousActionState sync;
evt->SetSyncState( sync );
PostEvent( evt );
sync.WaitForResult();
}
else
{
ScopedPtr<SysExecEvent> deleteMe( evt );
deleteMe->InvokeAction();
}
}
void pxEvtHandler::Idle()
{
DoIdle();
m_wakeup.WaitWithoutYield();
}
void pxEvtHandler::SetActiveThread()
{
m_OwnerThreadId = wxThread::GetCurrentId();
}
// --------------------------------------------------------------------------------------
// WaitingForThreadedTaskDialog
// --------------------------------------------------------------------------------------
class WaitingForThreadedTaskDialog
: public wxDialogWithHelpers
{
private:
typedef wxDialogWithHelpers _parent;
protected:
PersistentThread* m_thread;
public:
WaitingForThreadedTaskDialog( PersistentThread* thr, wxWindow* parent, const wxString& title, const wxString& content );
virtual ~WaitingForThreadedTaskDialog() throw() {}
protected:
void OnCancel_Clicked( wxCommandEvent& evt );
void OnTerminateApp_Clicked( wxCommandEvent& evt );
};
// --------------------------------------------------------------------------------------
// WaitingForThreadedTaskDialog Implementations
// --------------------------------------------------------------------------------------
WaitingForThreadedTaskDialog::WaitingForThreadedTaskDialog( PersistentThread* thr, wxWindow* parent, const wxString& title, const wxString& content )
: wxDialogWithHelpers( parent, title, wxVERTICAL )
{
m_thread = thr;
m_idealWidth = 500;
*this += Heading( content );
*this += 15;
*this += Heading( _("Press Cancel to attempt to cancel the action.") );
*this += Heading( _("Press Terminate to kill PCSX2 immediately.") );
*this += new wxButton( this, wxID_CANCEL );
*this += new wxButton( this, wxID_ANY, _("Terminate App") );
}
void WaitingForThreadedTaskDialog::OnCancel_Clicked( wxCommandEvent& evt )
{
evt.Skip();
if( !m_thread ) return;
m_thread->Cancel( false );
if( wxWindow* cancel = FindWindowById( wxID_CANCEL ) ) cancel->Disable();
}
void WaitingForThreadedTaskDialog::OnTerminateApp_Clicked( wxCommandEvent& evt )
{
// (note: SIGTERM is a "handled" kill that performs shutdown stuff, which typically just crashes anyway)
wxKill( wxGetProcessId(), wxSIGKILL );
}
// --------------------------------------------------------------------------------------
// ExecutorThread Implementations
// --------------------------------------------------------------------------------------
ExecutorThread::ExecutorThread( pxEvtHandler* evthandler )
{
m_EvtHandler = evthandler;
}
void ExecutorThread::ShutdownQueue()
{
if( !m_EvtHandler || m_EvtHandler->IsShuttingDown() ) return;
m_EvtHandler->ShutdownQueue();
Block();
}
void ExecutorThread::PostEvent( SysExecEvent* evt )
{
if( !pxAssert( m_EvtHandler ) ) return;
m_EvtHandler->PostEvent( evt );
}
void ExecutorThread::PostEvent( const SysExecEvent& evt )
{
if( !pxAssert( m_EvtHandler ) ) return;
m_EvtHandler->PostEvent( evt );
}
void ExecutorThread::ProcessEvent( SysExecEvent* evt )
{
if( m_EvtHandler )
m_EvtHandler->ProcessEvent( evt );
else
{
ScopedPtr<SysExecEvent> deleteMe( evt );
deleteMe->InvokeAction();
}
}
void ExecutorThread::ProcessEvent( SysExecEvent& evt )
{
if( m_EvtHandler )
m_EvtHandler->ProcessEvent( evt );
else
evt.InvokeAction();
}
void ExecutorThread::OnStart()
{
//if( !m_ExecutorTimer )
// m_ExecutorTimer = new wxTimer( wxTheApp, pxEvt_ThreadTaskTimeout_SysExec );
m_name = L"SysExecutor";
_parent::OnStart();
}
void ExecutorThread::ExecuteTaskInThread()
{
if( !pxAssertDev( m_EvtHandler, "Gimme a damn Event Handler first, object whore." ) ) return;
m_EvtHandler->SetActiveThread();
while( true )
{
if( !pxAssertDev( m_EvtHandler, "Event handler has been deallocated during SysExecutor thread execution." ) ) return;
m_EvtHandler->Idle();
m_EvtHandler->ProcessPendingEvents();
if( m_EvtHandler->IsShuttingDown() ) break;
}
}
void ExecutorThread::OnCleanupInThread()
{
//wxGetApp().PostCommand( m_task, pxEvt_ThreadTaskComplete );
_parent::OnCleanupInThread();
}
// --------------------------------------------------------------------------------------
// Threaded Event Handlers (Pcsx2App)
// --------------------------------------------------------------------------------------
// This event is called when the SysExecutorThread's timer triggers, which means the
// VM/system task has taken an oddly long period of time to complete. The task is able
// to invoke a modal dialog from here that will grant the user some options for handling
// the unresponsive thread.
void Pcsx2App::OnSysExecutorTaskTimeout( wxTimerEvent& evt )
{
if( !SysExecutorThread.IsRunning() ) return;
//BaseThreadedInvocation* task = SysExecutorThread.GetTask();
//if( !task ) return;
//task->ShowModalStatus();
}

View File

@ -15,6 +15,8 @@
#include "PrecompiledHeader.h"
#include "MainFrame.h"
#include "GSFrame.h"
#include "GS.h"
#include "MSWstuff.h"
@ -295,6 +297,8 @@ void GSFrame::CoreThread_OnSuspended()
// Could stop the timer outright here, tho no harm in having an occasional
// update here or there, just in case some state info changes while emu is suspended.
m_timer_UpdateTitle.Start( TitleBarUpdateMs );
if( g_Conf->GSWindow.CloseOnEsc ) Hide();
}
// overrides base Show behavior.
@ -331,7 +335,7 @@ void GSFrame::AppStatusEvent_OnSettingsApplied()
{
if( IsBeingDeleted() ) return;
ShowFullScreen( g_Conf->GSWindow.DefaultToFullscreen );
Show( !g_Conf->GSWindow.CloseOnEsc || ((g_plugins==NULL) || !SysHasValidState()) );
Show( !g_Conf->GSWindow.CloseOnEsc || !CorePlugins.IsOpen(PluginId_GS) || !SysHasValidState() );
if( wxStaticText* label = GetLabel_OutputDisabled() )
label->Show( !EmuConfig.GS.DisableOutput );

120
pcsx2/gui/GSFrame.h Normal file
View File

@ -0,0 +1,120 @@
/* 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 "App.h"
#include "CpuUsageProvider.h"
enum LimiterModeType
{
Limit_Nominal,
Limit_Turbo,
Limit_Slomo,
};
extern LimiterModeType g_LimiterMode;
// --------------------------------------------------------------------------------------
// GSPanel
// --------------------------------------------------------------------------------------
class GSPanel : public wxWindow, public EventListener_AppStatus
{
typedef wxWindow _parent;
protected:
AcceleratorDictionary m_Accels;
wxTimer m_HideMouseTimer;
bool m_CursorShown;
bool m_HasFocus;
public:
GSPanel( wxWindow* parent );
virtual ~GSPanel() throw();
void DoResize();
void DoShowMouse();
protected:
void AppStatusEvent_OnSettingsApplied();
#ifdef __WXMSW__
virtual WXLRESULT MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam);
#endif
void InitDefaultAccelerators();
void OnCloseWindow( wxCloseEvent& evt );
void OnResize(wxSizeEvent& event);
void OnShowMouse( wxMouseEvent& evt );
void OnHideMouseTimeout( wxTimerEvent& evt );
void OnKeyDown( wxKeyEvent& evt );
void OnFocus( wxFocusEvent& evt );
void OnFocusLost( wxFocusEvent& evt );
};
// --------------------------------------------------------------------------------------
// GSFrame
// --------------------------------------------------------------------------------------
class GSFrame : public wxFrame,
public EventListener_AppStatus,
public EventListener_CoreThread
{
typedef wxFrame _parent;
protected:
wxTimer m_timer_UpdateTitle;
wxWindowID m_id_gspanel;
wxWindowID m_id_OutputDisabled;
wxStaticText* m_label_Disabled;
wxStatusBar* m_statusbar;
CpuUsageProvider m_CpuUsage;
public:
GSFrame(wxWindow* parent, const wxString& title);
virtual ~GSFrame() throw();
GSPanel* GetViewport();
void SetFocus();
bool Show( bool shown=true );
wxStaticText* GetLabel_OutputDisabled() const;
protected:
void OnCloseWindow( wxCloseEvent& evt );
void OnMove( wxMoveEvent& evt );
void OnResize( wxSizeEvent& evt );
void OnActivate( wxActivateEvent& evt );
void OnUpdateTitle( wxTimerEvent& evt );
void AppStatusEvent_OnSettingsApplied();
void CoreThread_OnResumed();
void CoreThread_OnSuspended();
};
// --------------------------------------------------------------------------------------
// s* macros! ['s' stands for 'shortcut']
// --------------------------------------------------------------------------------------
// Use these for "silent fail" invocation of PCSX2 Application-related constructs. If the
// construct (albeit wxApp, MainFrame, CoreThread, etc) is null, the requested method will
// not be invoked, and an optional "else" clause can be affixed for handling the end case.
//
// See App.h (sApp) for more details.
//
#define sGSFrame \
if( GSFrame* __gsframe_ = wxGetApp().GetGsFramePtr() ) (*__gsframe_)

View File

@ -15,6 +15,8 @@
#include "PrecompiledHeader.h"
#include "MainFrame.h"
#include "GSFrame.h"
#include "HostGui.h"
#include "AppSaveStates.h"
#include "GS.h"
@ -91,7 +93,7 @@ namespace Implementations
g_Conf->EmuOptions.GS.LimitScalar = g_Conf->Framerate.TurboScalar;
Console.WriteLn("(FrameLimiter) Turbo ENABLED." );
}
pauser.Resume();
pauser.AllowResume();
}
void Framelimiter_SlomoToggle()
@ -117,7 +119,7 @@ namespace Implementations
Console.WriteLn("(FrameLimiter) SlowMotion ENABLED." );
g_Conf->EmuOptions.GS.FrameLimitEnable = true;
}
pauser.Resume();
pauser.AllowResume();
}
void Framelimiter_MasterToggle()
@ -126,7 +128,7 @@ namespace Implementations
g_Conf->EmuOptions.GS.FrameLimitEnable = !g_Conf->EmuOptions.GS.FrameLimitEnable;
GSsetVsync( g_Conf->EmuOptions.GS.FrameLimitEnable && g_Conf->EmuOptions.GS.VsyncEnable );
Console.WriteLn("(FrameLimiter) %s.", g_Conf->EmuOptions.GS.FrameLimitEnable ? "ENABLED" : "DISABLED" );
pauser.Resume();
pauser.AllowResume();
}
void Sys_Suspend()
@ -142,13 +144,11 @@ namespace Implementations
void Sys_TakeSnapshot()
{
GSmakeSnapshot( g_Conf->Folders.Snapshots.ToAscii().data() );
GSmakeSnapshot( g_Conf->Folders.Snapshots.ToAscii() );
}
void Sys_RenderToggle()
{
if( g_plugins == NULL ) return;
SaveSinglePluginHelper helper( PluginId_GS );
renderswitch = !renderswitch;
}

View File

@ -15,6 +15,7 @@
#include "PrecompiledHeader.h"
#include "App.h"
#include "MainFrame.h"
#include "IsoDropTarget.h"
#include "Dialogs/ModalPopups.h"
@ -34,7 +35,7 @@ wxString GetMsg_ConfirmSysReset()
bool IsoDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames)
{
ScopedCoreThreadSuspend stopped_core;
ScopedCoreThreadPopup stopped_core;
if( filenames.GetCount() > 1 )
{
@ -53,7 +54,7 @@ bool IsoDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filen
wxFileInputStream filechk( filenames[0] );
if( !filechk.IsOk() )
throw Exception::CreateStream( filenames[0] );
throw Exception::CannotCreateStream( filenames[0] );
u8 ident[16];
filechk.Read( ident, 16 );
@ -82,8 +83,9 @@ bool IsoDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filen
{
sApp.SysExecute( g_Conf->CdvdSource, g_Conf->CurrentELF );
}
else
stopped_core.AllowResume();
stopped_core.Resume();
return true;
}
}
@ -97,45 +99,18 @@ bool IsoDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filen
// hack ;)
isoFile iso;
memzero( iso );
iso.handle = _openfile( filenames[0].ToUTF8(), O_RDONLY);
memzero(iso);
iso.handle = _openfile(filenames[0].ToUTF8(), O_RDONLY);
if( iso.handle == NULL )
throw Exception::CreateStream( filenames[0] );
throw Exception::CannotCreateStream( filenames[0] );
if (isoDetect(&iso))
{
Console.WriteLn( L"(Drag&Drop) Found valid ISO file type!" );
wxWindowID result = wxID_RESET;
if( SysHasValidState() )
{
wxDialogWithHelpers dialog( m_WindowBound, _("Confirm PS2 Reset"), wxVERTICAL );
dialog += dialog.Heading(_("You have dropped the following ISO image into PCSX2:\n\n") +
filenames[0] + L"\n\n" +
_("Do you want to swap discs or boot the new image (via system reset)?")
);
result = pxIssueConfirmation( dialog, MsgButtons().Reset().Cancel().Custom(_("Swap Disc")), L"DragDrop:BootIso" );
}
if( result != wxID_CANCEL )
{
SysUpdateIsoSrcFile( filenames[0] );
if( result != wxID_RESET )
{
CoreThread.ChangeCdvdSource( CDVDsrc_Iso );
}
else
{
sApp.SysExecute( CDVDsrc_Iso );
}
}
SwapOrReset_Iso(m_WindowBound, stopped_core, filenames[0], _("You have dropped the following ISO image into PCSX2:\n\n"));
}
_closefile( iso.handle );
stopped_core.Resume();
return true;
}

View File

@ -34,3 +34,7 @@ public:
virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames);
};
extern wxString GetMsg_ConfirmSysReset();
extern wxWindowID SwapOrReset_Iso( wxWindow* owner, IScopedCoreThread& core_control, const wxString& isoFilename, const wxString& descpart1 );

View File

@ -14,7 +14,8 @@
*/
#include "PrecompiledHeader.h"
#include "MainFrame.h"
#include "GSFrame.h"
#include "MSWstuff.h"
#include <wx/listbook.h>
#include <wx/listctrl.h>

View File

@ -92,15 +92,16 @@ void MainEmuFrame::OnCloseWindow(wxCloseEvent& evt)
//evt.Veto( true );
if( StateCopy_InvokeOnSaveComplete( new InvokeAction_MenuCommand( MenuId_Exit ) ) ) return;
}
m_menuCDVD.Remove( MenuId_IsoSelector );
//m_menuCDVD.Delete( MenuId_IsoSelector );
wxGetApp().PrepForExit();
sApp.OnMainFrameClosed( GetId() );
if( m_menubar.FindItem(MenuId_IsoSelector) )
m_menuCDVD.Remove(MenuId_IsoSelector);
RemoveEventHandler( &wxGetApp().GetRecentIsoManager() );
evt.Skip();
}
@ -169,14 +170,14 @@ void MainEmuFrame::ConnectMenus()
ConnectMenu( MenuId_PluginBase_Settings + (i*PluginMenuId_Interval), Menu_ConfigPlugin_Click);
ConnectMenu( MenuId_Boot_CDVD, Menu_BootCdvd_Click );
ConnectMenu( MenuId_Boot_CDVD2, Menu_BootCdvd2_Click );
ConnectMenu( MenuId_Boot_ELF, Menu_OpenELF_Click );
ConnectMenu( MenuId_IsoBrowse, Menu_IsoBrowse_Click );
ConnectMenu( MenuId_SkipBiosToggle, Menu_SkipBiosToggle_Click );
ConnectMenu( MenuId_EnablePatches, Menu_EnablePatches_Click );
ConnectMenu( MenuId_Exit, Menu_Exit_Click );
ConnectMenu( MenuId_Sys_SuspendResume, Menu_SuspendResume_Click );
ConnectMenu( MenuId_Sys_Reset, Menu_SysReset_Click );
ConnectMenu( MenuId_Sys_Restart, Menu_SysReset_Click );
ConnectMenu( MenuId_Sys_Shutdown, Menu_SysShutdown_Click );
ConnectMenu( MenuId_State_LoadOther, Menu_LoadStateOther_Click );
@ -229,14 +230,12 @@ void MainEmuFrame::DispatchEvent( const PluginEventType& plugin_evt )
}
else if( plugin_evt == CorePlugins_Loaded )
{
if( !pxAssertDev( g_plugins!=NULL, wxNullChar ) ) return;
for( int i=0; i<PluginId_Count; ++i )
m_PluginMenuPacks[i].OnLoaded();
// bleh this makes the menu too cluttered. --air
//m_menuCDVD.SetLabel( MenuId_Src_Plugin, wxsFormat( L"%s (%s)", _("Plugin"),
// g_plugins->GetName( PluginId_CDVD ).c_str() ) );
// GetCorePlugins().GetName( PluginId_CDVD ).c_str() ) );
}
}
@ -304,9 +303,9 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title)
// Initial menubar setup. This needs to be done first so that the menu bar's visible size
// can be factored into the window size (which ends up being background+status+menus)
m_menubar.Append( &m_menuBoot, _("&Boot") );
m_menubar.Append( &m_menuCDVD, _("CD&VD") );
//m_menubar.Append( &m_menuBoot, _("&Boot") );
m_menubar.Append( &m_menuSys, _("&System") );
m_menubar.Append( &m_menuCDVD, _("CD&VD") );
m_menubar.Append( &m_menuConfig, _("&Config") );
m_menubar.Append( &m_menuMisc, _("&Misc") );
#ifdef PCSX2_DEVBUILD
@ -364,37 +363,18 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title)
InitLogBoxPosition( g_Conf->ProgLogBox );
// ------------------------------------------------------------------------
// Some of the items in the System menu are configured by the UpdateCoreStatus() method.
m_menuSys.Append(MenuId_Boot_CDVD, _("Initializing..."));
m_menuBoot.Append(MenuId_Boot_CDVD, _("Run CDVD"),
_("For booting DVD discs or Isos, depending on the configured CDVD source."));
m_menuSys.Append(MenuId_Boot_CDVD2, _("Initializing..."));
m_menuBoot.Append(MenuId_Boot_ELF, _("Run ELF File..."),
_("For running raw binaries directly"));
m_menuBoot.AppendSeparator();
m_menuBoot.Append(MenuId_Exit, _("Exit"),
_("Closing PCSX2 may be hazardous to your health"));
// ------------------------------------------------------------------------
wxMenu& isoRecents( wxGetApp().GetRecentIsoMenu() );
//m_menuCDVD.AppendSeparator();
m_menuCDVD.Append( MenuId_IsoSelector, _("Iso Selector"), &isoRecents );
m_menuCDVD.Append( GetPluginMenuId_Settings(PluginId_CDVD), _("Plugin Menu"), m_PluginMenuPacks[PluginId_CDVD] );
m_menuCDVD.AppendSeparator();
m_menuCDVD.Append( MenuId_Src_Iso, _("Iso"), _("Makes the specified ISO image the CDVD source."), wxITEM_RADIO );
m_menuCDVD.Append( MenuId_Src_Plugin, _("Plugin"), _("Uses an external plugin as the CDVD source."), wxITEM_RADIO );
m_menuCDVD.Append( MenuId_Src_NoDisc, _("No disc"), _("Use this to boot into your virtual PS2's BIOS configuration."), wxITEM_RADIO );
m_menuCDVD.AppendSeparator();
m_menuCDVD.Append( MenuId_SkipBiosToggle,_("Enable Skip BIOS Hack"),
_("Skips PS2 splash screens when booting from Iso or CDVD media"), wxITEM_CHECK );
// ------------------------------------------------------------------------
m_menuSys.Append(MenuId_Sys_SuspendResume, _("Suspend") )->Enable( SysHasValidState() );
m_menuSys.Append(MenuId_Boot_ELF, _("Run ELF..."),
_("For running raw PS2 binaries directly"));
m_menuSys.AppendSeparator();
m_menuSys.Append(MenuId_Sys_SuspendResume, _("Initializing..."));
m_menuSys.AppendSeparator();
//m_menuSys.Append(MenuId_Sys_Close, _("Close"),
// _("Stops emulation and closes the GS window."));
@ -408,12 +388,30 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title)
wxEmptyString, wxITEM_CHECK);
m_menuSys.AppendSeparator();
m_menuSys.Append(MenuId_Sys_Reset, _("Reset"),
_("Resets the VM state and re-runs current CDVD image."));
m_menuSys.Append(MenuId_Sys_Shutdown, _("Shutdown"),
_("Wipes all internal VM states and shuts down plugins."));
m_menuSys.Append(MenuId_Exit, _("Exit"),
_("Closing PCSX2 may be hazardous to your health"));
// ------------------------------------------------------------------------
wxMenu& isoRecents( wxGetApp().GetRecentIsoMenu() );
//m_menuCDVD.AppendSeparator();
m_menuCDVD.Append( MenuId_IsoSelector, _("Iso Selector"), &isoRecents );
m_menuCDVD.Append( GetPluginMenuId_Settings(PluginId_CDVD), _("Plugin Menu"), m_PluginMenuPacks[PluginId_CDVD] );
m_menuCDVD.AppendSeparator();
m_menuCDVD.Append( MenuId_Src_Iso, _("Iso"), _("Makes the specified ISO image the CDVD source."), wxITEM_RADIO );
m_menuCDVD.Append( MenuId_Src_Plugin, _("Plugin"), _("Uses an external plugin as the CDVD source."), wxITEM_RADIO );
m_menuCDVD.Append( MenuId_Src_NoDisc, _("No disc"), _("Use this to boot into your virtual PS2's BIOS configuration."), wxITEM_RADIO );
//m_menuCDVD.AppendSeparator();
//m_menuCDVD.Append( MenuId_SkipBiosToggle,_("Enable BOOT2 injection"),
// _("Skips PS2 splash screens when booting from Iso or DVD media"), wxITEM_CHECK );
// ------------------------------------------------------------------------
m_menuConfig.Append(MenuId_Config_SysSettings, _("Emulation &Settings") );
@ -478,7 +476,9 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title)
Connect( wxEVT_ACTIVATE, wxActivateEventHandler (MainEmuFrame::OnActivate) );
PushEventHandler( &wxGetApp().GetRecentIsoManager() );
SetDropTarget( new IsoDropTarget( this ) );
ApplyCoreStatus();
}
MainEmuFrame::~MainEmuFrame() throw()
@ -486,7 +486,7 @@ MainEmuFrame::~MainEmuFrame() throw()
if( m_RestartEmuOnDelete )
{
sApp.SetExitOnFrameDelete( false );
sApp.PostMethod( &Pcsx2App::DetectCpuAndUserMode );
sApp.PostAppMethod( &Pcsx2App::DetectCpuAndUserMode );
sApp.WipeUserModeSettings();
}
}
@ -520,38 +520,71 @@ void MainEmuFrame::ApplyCoreStatus()
{
wxMenuBar& menubar( *GetMenuBar() );
wxMenuItem& susres( *menubar.FindItem( MenuId_Sys_SuspendResume ) );
if( !pxAssertMsg( &susres!=NULL, "Suspend/Resume Menubar Item is NULL!" ) ) return;
wxMenuItem& susres (*menubar.FindItem( MenuId_Sys_SuspendResume ));
wxMenuItem& cdvd (*menubar.FindItem( MenuId_Boot_CDVD ));
wxMenuItem& cdvd2 (*menubar.FindItem( MenuId_Boot_CDVD2 ));
if( !pxAssertMsg( (&susres) && (&cdvd) && (&cdvd2), "Unexpected NULL Menubar Item!" ) ) return;
wxMenuItem* restart = menubar.FindItem( MenuId_Sys_Restart );
if( SysHasValidState() )
{
susres.Enable();
if( CoreThread.IsOpen() )
{
susres.SetHelp( _("Safely pauses emulation and preserves the PS2 state.") );
susres.SetText( _("Suspend") );
susres.SetText(_("Suspend"));
susres.SetHelp(_("Safely pauses emulation and preserves the PS2 state."));
}
else
{
susres.SetHelp( _("Resumes the suspended emulation state.") );
susres.SetText( _("Resume") );
susres.SetText(_("Resume"));
susres.SetHelp(_("Resumes the suspended emulation state."));
}
if( restart )
{
restart->SetText(_("Restart"));
restart->SetHelp(_("Simulates hardware reset of the PS2 virtual machine."));
}
cdvd.SetText(_("Reboot CDVD (full)"));
cdvd.SetHelp(_("Hard reset of the active VM."));
cdvd2.SetText(_("Reboot CDVD (fast)"));
cdvd2.SetHelp(_("Reboot using BOOT2 injection (skips splash screens)"));
}
else
{
susres.Enable( false );
susres.SetText(_("Suspend/Resume"));
susres.SetHelp( _("No emulation state is active; cannot suspend or resume.") );
if( restart )
{
restart->Enable( false );
restart->SetHelp( _("No emulation state is active; boot something first.") );
}
cdvd.SetText(_("Boot CDVD (full)"));
cdvd.SetHelp(_("Boot the VM using the current DVD or Iso source media"));
cdvd2.SetText(_("Boot CDVD (fast)"));
cdvd2.SetHelp(_("Use BOOT2 injection to skip PS2 startup and splash screens"));
// Old style...
//restart.SetHelp(_("Starts execution of the PS2 virtual machine."));
//restart.SetText(_("Start"));
}
menubar.Enable( MenuId_Sys_Reset, true );
menubar.Enable( MenuId_Sys_Shutdown, SysHasValidState() || (g_plugins!=NULL) );
menubar.Enable( MenuId_Sys_Shutdown, SysHasValidState() || CorePlugins.AreAnyInitialized() );
}
void MainEmuFrame::ApplySettings()
{
wxMenuBar& menubar( *GetMenuBar() );
menubar.Check( MenuId_SkipBiosToggle, g_Conf->EmuOptions.SkipBiosSplash );
menubar.Check( MenuId_EnablePatches, g_Conf->EmuOptions.EnablePatches );
menubar.Check( MenuId_CDVD_Info, g_Conf->EmuOptions.CdvdVerboseReads );
#ifdef __LINUX__
@ -615,8 +648,9 @@ void PerPluginMenuInfo::OnUnloaded()
void PerPluginMenuInfo::OnLoaded()
{
if( !CorePlugins.IsLoaded(PluginId) ) return;
MyMenu.SetLabel( GetPluginMenuId_Name(PluginId),
g_plugins->GetName( PluginId ) + L" " + g_plugins->GetVersion( PluginId )
CorePlugins.GetName( PluginId ) + L" " + CorePlugins.GetVersion( PluginId )
);
MyMenu.Enable( GetPluginMenuId_Settings(PluginId), true );
}

View File

@ -15,99 +15,11 @@
#pragma once
#include <wx/wx.h>
#include <wx/image.h>
#include <wx/docview.h>
#include "App.h"
#include "AppSaveStates.h"
#include "CpuUsageProvider.h"
enum LimiterModeType
{
Limit_Nominal,
Limit_Turbo,
Limit_Slomo,
};
extern LimiterModeType g_LimiterMode;
// --------------------------------------------------------------------------------------
// GSPanel
// --------------------------------------------------------------------------------------
class GSPanel : public wxWindow, public EventListener_AppStatus
{
typedef wxWindow _parent;
protected:
AcceleratorDictionary m_Accels;
wxTimer m_HideMouseTimer;
bool m_CursorShown;
bool m_HasFocus;
public:
GSPanel( wxWindow* parent );
virtual ~GSPanel() throw();
void DoResize();
void DoShowMouse();
protected:
void AppStatusEvent_OnSettingsApplied();
#ifdef __WXMSW__
virtual WXLRESULT MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam);
#endif
void InitDefaultAccelerators();
void OnCloseWindow( wxCloseEvent& evt );
void OnResize(wxSizeEvent& event);
void OnShowMouse( wxMouseEvent& evt );
void OnHideMouseTimeout( wxTimerEvent& evt );
void OnKeyDown( wxKeyEvent& evt );
void OnFocus( wxFocusEvent& evt );
void OnFocusLost( wxFocusEvent& evt );
};
// --------------------------------------------------------------------------------------
// GSFrame
// --------------------------------------------------------------------------------------
class GSFrame : public wxFrame,
public EventListener_AppStatus,
public EventListener_CoreThread
{
typedef wxFrame _parent;
protected:
wxTimer m_timer_UpdateTitle;
wxWindowID m_id_gspanel;
wxWindowID m_id_OutputDisabled;
wxStaticText* m_label_Disabled;
wxStatusBar* m_statusbar;
CpuUsageProvider m_CpuUsage;
public:
GSFrame(wxWindow* parent, const wxString& title);
virtual ~GSFrame() throw();
GSPanel* GetViewport();
void SetFocus();
bool Show( bool shown=true );
wxStaticText* GetLabel_OutputDisabled() const;
protected:
void OnCloseWindow( wxCloseEvent& evt );
void OnMove( wxMoveEvent& evt );
void OnResize( wxSizeEvent& evt );
void OnActivate( wxActivateEvent& evt );
void OnUpdateTitle( wxTimerEvent& evt );
void AppStatusEvent_OnSettingsApplied();
void CoreThread_OnResumed();
void CoreThread_OnSuspended();
};
#include <wx/image.h>
#include <wx/docview.h>
struct PluginMenuAddition
{
@ -162,18 +74,25 @@ public:
operator const wxMenu*() const { return &MyMenu; }
};
class InvokeAction_MenuCommand : public IActionInvocation
// --------------------------------------------------------------------------------------
// InvokeMenuCommand_OnSysStateUnlocked
// --------------------------------------------------------------------------------------
class InvokeMenuCommand_OnSysStateUnlocked
: public IEventListener_SysState
, public BaseDeletableObject
{
protected:
MenuIdentifiers m_menu_cmd;
public:
InvokeAction_MenuCommand( MenuIdentifiers menu_command )
InvokeMenuCommand_OnSysStateUnlocked( MenuIdentifiers menu_command )
{
m_menu_cmd = menu_command;
}
virtual ~InvokeMenuCommand_OnSysStateUnlocked() throw() {}
virtual void InvokeAction()
virtual void SaveStateAction_OnCreateFinished()
{
wxGetApp().PostMenuAction( m_menu_cmd );
}
@ -188,10 +107,6 @@ class MainEmuFrame : public wxFrame,
public EventListener_AppStatus
{
protected:
// EventListenerHelper_Plugins<MainEmuFrame> m_listener_plugins;
// EventListenerHelper_CoreThread<MainEmuFrame> m_listener_corethread;
// EventListenerHelper_AppStatus<MainEmuFrame> m_listener_appstatus;
bool m_RestartEmuOnDelete;
wxStatusBar& m_statusbar;
@ -248,10 +163,10 @@ protected:
void Menu_ResetAllSettings_Click(wxCommandEvent &event);
void Menu_IsoBrowse_Click(wxCommandEvent &event);
void Menu_SkipBiosToggle_Click(wxCommandEvent &event);
void Menu_EnablePatches_Click(wxCommandEvent &event);
void Menu_BootCdvd_Click(wxCommandEvent &event);
void Menu_BootCdvd2_Click(wxCommandEvent &event);
void Menu_OpenELF_Click(wxCommandEvent &event);
void Menu_CdvdSource_Click(wxCommandEvent &event);
void Menu_LoadStates_Click(wxCommandEvent &event);
@ -277,6 +192,7 @@ protected:
void Menu_PrintCDVD_Info(wxCommandEvent &event);
void Menu_ShowAboutBox(wxCommandEvent &event);
void _DoBootCdvd();
bool _DoSelectIsoBrowser( wxString& dest );
bool _DoSelectELFBrowser();

View File

@ -25,11 +25,10 @@
#include "Dialogs/LogOptionsDialog.h"
#include "IniInterface.h"
#include "IsoDropTarget.h"
using namespace Dialogs;
extern wxString GetMsg_ConfirmSysReset();
void MainEmuFrame::SaveEmuOptions()
{
if (wxConfigBase* conf = GetAppConfig())
@ -57,7 +56,6 @@ void MainEmuFrame::Menu_SelectBios_Click(wxCommandEvent &event)
static void WipeSettings()
{
UnloadPlugins();
wxGetApp().CleanupRestartable();
wxGetApp().CleanupResources();
@ -80,53 +78,31 @@ void MainEmuFrame::RemoveCdvdMenu()
m_menuCDVD.Remove( item );
}
class RestartEverything_WhenCoreThreadStops : public EventListener_CoreThread,
public virtual IDeletableObject
{
public:
RestartEverything_WhenCoreThreadStops() {}
virtual ~RestartEverything_WhenCoreThreadStops() throw() {}
protected:
virtual void CoreThread_OnStopped()
{
wxGetApp().DeleteObject( this );
WipeSettings();
}
};
class CancelCoreThread_WhenSaveStateDone :
public EventListener_CoreThread,
public IDeletableObject
{
public:
virtual ~CancelCoreThread_WhenSaveStateDone() throw() {}
void CoreThread_OnResumed()
{
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;
ScopedCoreThreadPopup suspender;
if( !Msgbox::OkCancel(
pxE( ".Popup Warning:DeleteSettings",
L"This command clears PCSX2 settings and allows you to re-run the First-Time Wizard. You will need to "
L"manually restart PCSX2 after this operation.\n\n"
L"WARNING!! Click OK to delete *ALL* settings for PCSX2 and force PCSX2 to shudown, losing any current emulation progress. Are you absolutely sure?"
L"\n\n(note: settings for plugins are unaffected)"
),
_("Reset all settings?") ) )
{
suspender.AllowResume();
return;
}
}
// Old 'auto-restart' method; avoids shutting down the PCSX2 process, but tends to
// be bug prone in odd ways (namely QueryPerformanceFrequency hangs for a number of
// seconds .. wtf?)
/*
m_RestartEmuOnDelete = true;
Destroy();
@ -145,21 +121,93 @@ void MainEmuFrame::Menu_ResetAllSettings_Click(wxCommandEvent &event)
{
WipeSettings();
}
*/
WipeSettings();
wxGetApp().PostMenuAction( MenuId_Exit );
}
void MainEmuFrame::Menu_CdvdSource_Click( wxCommandEvent &event )
// Return values:
// wxID_CANCEL - User canceled the action outright.
// wxID_RESET - User wants to reset the emu in addition to swap discs
// (anything else) - Standard swap, no reset. (hotswap!)
wxWindowID SwapOrReset_Iso( wxWindow* owner, IScopedCoreThread& core_control, const wxString& isoFilename, const wxString& descpart1 )
{
CDVD_SourceType newSource = CDVDsrc_NoDisc;
wxWindowID result = wxID_RESET;
switch( event.GetId() )
if( SysHasValidState() )
{
case MenuId_Src_Iso: newSource = CDVDsrc_Iso; break;
case MenuId_Src_Plugin: newSource = CDVDsrc_Plugin; break;
case MenuId_Src_NoDisc: newSource = CDVDsrc_NoDisc; break;
core_control.DisallowResume();
wxDialogWithHelpers dialog( owner, _("Confirm ISO image change"), wxVERTICAL );
jNO_DEFAULT
dialog += dialog.Heading(descpart1 +
isoFilename + L"\n\n" +
_("Do you want to swap discs or boot the new image (via system reset)?")
);
result = pxIssueConfirmation( dialog, MsgButtons().Reset().Cancel().Custom(_("Swap Disc")), L"DragDrop:BootSwapIso" );
}
CoreThread.ChangeCdvdSource( newSource );
if( result != wxID_CANCEL )
{
SysUpdateIsoSrcFile( isoFilename );
if( result != wxID_RESET )
{
Console.Indent().WriteLn( "HotSwapping to new ISO src image!" );
g_Conf->CdvdSource = CDVDsrc_Iso;
sMainFrame.UpdateIsoSrcSelection();
CoreThread.ChangeCdvdSource();
core_control.AllowResume();
}
else
{
core_control.DisallowResume();
sApp.SysExecute( CDVDsrc_Iso );
}
}
return result;
}
wxWindowID SwapOrReset_CdvdSrc( wxWindow* owner, CDVD_SourceType newsrc )
{
if(newsrc == g_Conf->CdvdSource) return wxID_CANCEL;
wxWindowID result = wxID_CANCEL;
ScopedCoreThreadPopup core;
if( SysHasValidState() )
{
wxDialogWithHelpers dialog( owner, _("Confirm CDVD source change"), wxVERTICAL );
wxString changeMsg;
changeMsg.Printf(_("You've selected to switch the CDVD source from %s to %s."),
CDVD_SourceLabels[g_Conf->CdvdSource], CDVD_SourceLabels[newsrc] );
dialog += dialog.Heading(changeMsg + L"\n\n" +
_("Do you want to swap discs or boot the new image (system reset)?")
);
result = pxIssueConfirmation( dialog, MsgButtons().Reset().Cancel().Custom(_("Swap Disc")), L"DragDrop:BootSwapIso" );
}
if( result != wxID_CANCEL )
{
if( result != wxID_RESET )
{
Console.Indent().WriteLn( L"(CdvdSource) HotSwapping CDVD source types from %s to %s.", CDVD_SourceLabels[g_Conf->CdvdSource], CDVD_SourceLabels[newsrc] );
g_Conf->CdvdSource = newsrc;
CoreThread.ChangeCdvdSource();
core.AllowResume();
}
else
{
core.DisallowResume();
g_Conf->CdvdSource = newsrc;
sApp.SysExecute( newsrc );
}
}
return result;
}
// Returns FALSE if the user canceled the action.
@ -203,9 +251,9 @@ bool MainEmuFrame::_DoSelectELFBrowser()
return false;
}
void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event )
void MainEmuFrame::_DoBootCdvd()
{
ScopedCoreThreadSuspend core;
ScopedCoreThreadPause paused_core;
if( g_Conf->CdvdSource == CDVDsrc_Iso )
{
@ -232,7 +280,7 @@ void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event )
wxString result;
if( !_DoSelectIsoBrowser( result ) )
{
core.Resume();
paused_core.AllowResume();
return;
}
@ -248,30 +296,57 @@ void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event )
if( !confirmed )
{
core.Resume();
paused_core.AllowResume();
return;
}
}
sApp.SysReset();
sApp.SysExecute( g_Conf->CdvdSource );
}
void MainEmuFrame::Menu_CdvdSource_Click( wxCommandEvent &event )
{
CDVD_SourceType newsrc = CDVDsrc_NoDisc;
switch( event.GetId() )
{
case MenuId_Src_Iso: newsrc = CDVDsrc_Iso; break;
case MenuId_Src_Plugin: newsrc = CDVDsrc_Plugin; break;
case MenuId_Src_NoDisc: newsrc = CDVDsrc_NoDisc; break;
jNO_DEFAULT
}
SwapOrReset_CdvdSrc(this, newsrc);
}
void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event )
{
g_Conf->EmuOptions.UseBOOT2Injection = false;
_DoBootCdvd();
}
void MainEmuFrame::Menu_BootCdvd2_Click( wxCommandEvent &event )
{
g_Conf->EmuOptions.UseBOOT2Injection = true;
_DoBootCdvd();
}
static wxString GetMsg_IsoImageChanged()
{
return _("You have selected the following ISO image into PCSX2:\n\n");
}
void MainEmuFrame::Menu_IsoBrowse_Click( wxCommandEvent &event )
{
ScopedCoreThreadSuspend core;
wxString result;
ScopedCoreThreadPopup core;
wxString isofile;
if( _DoSelectIsoBrowser( result ) )
if( !_DoSelectIsoBrowser(isofile) ||
(wxID_CANCEL == SwapOrReset_Iso(this, core, isofile, GetMsg_IsoImageChanged())) )
{
// This command does an on-the-fly change of CD media without automatic reset.
// (useful for disc swapping)
SysUpdateIsoSrcFile( result );
AppSaveSettings();
core.AllowResume();
return;
}
core.Resume();
}
void MainEmuFrame::Menu_MultitapToggle_Click( wxCommandEvent& )
@ -284,12 +359,6 @@ void MainEmuFrame::Menu_MultitapToggle_Click( wxCommandEvent& )
//evt.Skip();
}
void MainEmuFrame::Menu_SkipBiosToggle_Click( wxCommandEvent& )
{
g_Conf->EmuOptions.SkipBiosSplash = GetMenuBar()->IsChecked( MenuId_SkipBiosToggle );
SaveEmuOptions();
}
void MainEmuFrame::Menu_EnablePatches_Click( wxCommandEvent& )
{
g_Conf->EmuOptions.EnablePatches = GetMenuBar()->IsChecked( MenuId_EnablePatches );
@ -298,13 +367,13 @@ void MainEmuFrame::Menu_EnablePatches_Click( wxCommandEvent& )
void MainEmuFrame::Menu_OpenELF_Click(wxCommandEvent&)
{
bool resume = CoreThread.Suspend();
ScopedCoreThreadClose stopped_core;
if( _DoSelectELFBrowser() )
{
sApp.SysExecute( g_Conf->CdvdSource, g_Conf->CurrentELF );
}
if( resume ) CoreThread.Resume();
stopped_core.AllowResume();
}
void MainEmuFrame::Menu_LoadStates_Click(wxCommandEvent &event)
@ -334,6 +403,43 @@ void MainEmuFrame::Menu_Exit_Click(wxCommandEvent &event)
Close();
}
class SysExecEvent_ToggleSuspend : public SysExecEvent
{
public:
virtual ~SysExecEvent_ToggleSuspend() throw() {}
wxString GetEventName() const { return L"ToggleSuspendResume"; }
protected:
void _DoInvoke()
{
if( CoreThread.IsOpen() )
CoreThread.Suspend();
else
CoreThread.Resume();
}
};
class SysExecEvent_Restart : public SysExecEvent
{
public:
virtual ~SysExecEvent_Restart() throw() {}
wxString GetEventName() const { return L"SysRestart"; }
wxString GetEventMessage() const
{
return _("Restarting PS2 Virtual Machine...");
}
protected:
void _DoInvoke()
{
sApp.SysShutdown();
sApp.SysExecute();
}
};
void MainEmuFrame::Menu_SuspendResume_Click(wxCommandEvent &event)
{
if( !SysHasValidState() ) return;
@ -343,29 +449,21 @@ void MainEmuFrame::Menu_SuspendResume_Click(wxCommandEvent &event)
// engaged successfully).
GetMenuBar()->Enable( MenuId_Sys_SuspendResume, false );
if( !CoreThread.Suspend() )
{
sApp.SysExecute();
}
GetSysExecutorThread().PostEvent( new SysExecEvent_ToggleSuspend() );
}
void MainEmuFrame::Menu_SysReset_Click(wxCommandEvent &event)
{
if( StateCopy_InvokeOnCopyComplete( new InvokeAction_MenuCommand(MenuId_Sys_Reset) ) ) return;
sApp.SysReset();
sApp.SysExecute();
//GetMenuBar()->Enable( MenuId_Sys_Reset, true );
UI_DisableSysReset();
GetSysExecutorThread().PostEvent( new SysExecEvent_Restart() );
}
void MainEmuFrame::Menu_SysShutdown_Click(wxCommandEvent &event)
{
if( !SysHasValidState() && g_plugins == NULL ) return;
if( StateCopy_InvokeOnCopyComplete( new InvokeAction_MenuCommand(MenuId_Sys_Shutdown) ) ) return;
if( !SysHasValidState() && !CorePlugins.AreAnyInitialized() ) return;
sApp.SysReset();
GetMenuBar()->Enable( MenuId_Sys_Shutdown, false );
UI_DisableSysShutdown();
sApp.SysShutdown();
}
void MainEmuFrame::Menu_ConfigPlugin_Click(wxCommandEvent &event)
@ -379,12 +477,13 @@ void MainEmuFrame::Menu_ConfigPlugin_Click(wxCommandEvent &event)
if( !pxAssertDev( (eventId >= 0) || (pid < PluginId_Count), "Invalid plugin identifier passed to ConfigPlugin event handler." ) ) return;
LoadPluginsImmediate();
if( g_plugins == NULL ) return;
// This could probably just load a single plugin as needed now, but for design safety
// I'm leaving it force-load everything until the individual plugin management is
// better tested.
wxWindowDisabler disabler;
SaveSinglePluginHelper helper( pid );
g_plugins->Configure( pid );
GetCorePlugins().Configure( pid );
}
void MainEmuFrame::Menu_Debug_Open_Click(wxCommandEvent &event)

View File

@ -26,7 +26,7 @@ using namespace pxSizerFlags;
//
static int pxMessageDialog( const wxString& caption, const wxString& content, const MsgButtons& buttons )
{
if( !AffinityAssert_AllowFromMain() ) return wxID_CANCEL;
if( !AffinityAssert_AllowFrom_MainUI() ) return wxID_CANCEL;
// fixme: If the emulator is currently active and is running in exclusive mode (forced
// fullscreen), then we need to either:
@ -43,54 +43,31 @@ static int pxMessageDialog( const wxString& caption, const wxString& content, co
// --------------------------------------------------------------------------------------
// BaseMessageBoxEvent Implementation
// --------------------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS( BaseMessageBoxEvent, wxEvent )
IMPLEMENT_DYNAMIC_CLASS( BaseMessageBoxEvent, pxInvokeActionEvent )
BaseMessageBoxEvent::BaseMessageBoxEvent( int msgtype, const wxString& content )
: wxEvent( 0, msgtype )
, m_Content( content )
BaseMessageBoxEvent::BaseMessageBoxEvent( const wxString& content, SynchronousActionState& instdata )
: m_Content( content )
{
m_Instdata = NULL;
m_state = &instdata;
}
BaseMessageBoxEvent::BaseMessageBoxEvent( MsgboxEventResult& instdata, const wxString& content )
: wxEvent( 0, pxEvt_MessageBox )
, m_Instdata( &instdata )
, m_Content( content )
{
}
BaseMessageBoxEvent::BaseMessageBoxEvent( const wxString& content )
: wxEvent( 0, pxEvt_MessageBox )
, m_Instdata( NULL )
, m_Content( content )
BaseMessageBoxEvent::BaseMessageBoxEvent( const wxString& content, SynchronousActionState* instdata )
: m_Content( content )
{
m_state = instdata;
}
BaseMessageBoxEvent::BaseMessageBoxEvent( const BaseMessageBoxEvent& event )
: wxEvent( event )
, m_Instdata( event.m_Instdata )
, m_Content( event.m_Content )
: m_Content( event.m_Content )
{
}
BaseMessageBoxEvent& BaseMessageBoxEvent::SetInstData( MsgboxEventResult& instdata )
{
m_Instdata = &instdata;
return *this;
m_state = event.m_state;
}
// Thread Safety: Must be called from the GUI thread ONLY.
void BaseMessageBoxEvent::IssueDialog()
void BaseMessageBoxEvent::_DoInvoke()
{
AffinityAssert_AllowFromMain();
int result = _DoDialog();
if( m_Instdata != NULL )
{
m_Instdata->result = result;
m_Instdata->WaitForMe.Post();
}
if( m_state ) m_state->PostResult( result );
}
int BaseMessageBoxEvent::_DoDialog() const
@ -104,20 +81,15 @@ int BaseMessageBoxEvent::_DoDialog() const
// --------------------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS( pxMessageBoxEvent, BaseMessageBoxEvent )
pxMessageBoxEvent::pxMessageBoxEvent( int msgtype )
: BaseMessageBoxEvent( msgtype )
{
}
pxMessageBoxEvent::pxMessageBoxEvent( MsgboxEventResult& instdata, const wxString& title, const wxString& content, const MsgButtons& buttons )
: BaseMessageBoxEvent( instdata, content )
pxMessageBoxEvent::pxMessageBoxEvent( const wxString& title, const wxString& content, const MsgButtons& buttons, SynchronousActionState& instdata )
: BaseMessageBoxEvent( content, instdata )
, m_Title( title )
, m_Buttons( buttons )
{
}
pxMessageBoxEvent::pxMessageBoxEvent( const wxString& title, const wxString& content, const MsgButtons& buttons )
: BaseMessageBoxEvent( content )
pxMessageBoxEvent::pxMessageBoxEvent( const wxString& title, const wxString& content, const MsgButtons& buttons, SynchronousActionState* instdata )
: BaseMessageBoxEvent( content, instdata )
, m_Title( title )
, m_Buttons( buttons )
{
@ -130,12 +102,6 @@ pxMessageBoxEvent::pxMessageBoxEvent( const pxMessageBoxEvent& event )
{
}
pxMessageBoxEvent& pxMessageBoxEvent::SetInstData( MsgboxEventResult& instdata )
{
_parent::SetInstData( instdata );
return *this;
}
int pxMessageBoxEvent::_DoDialog() const
{
return pxMessageDialog( m_Content, m_Title, m_Buttons );
@ -146,19 +112,14 @@ int pxMessageBoxEvent::_DoDialog() const
// --------------------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS( pxAssertionEvent, BaseMessageBoxEvent )
pxAssertionEvent::pxAssertionEvent()
: BaseMessageBoxEvent( )
{
}
pxAssertionEvent::pxAssertionEvent( MsgboxEventResult& instdata, const wxString& content, const wxString& trace )
: BaseMessageBoxEvent( instdata, content )
pxAssertionEvent::pxAssertionEvent( const wxString& content, const wxString& trace, SynchronousActionState& instdata )
: BaseMessageBoxEvent( content, instdata )
, m_Stacktrace( trace )
{
}
pxAssertionEvent::pxAssertionEvent( const wxString& content, const wxString& trace )
: BaseMessageBoxEvent( content )
pxAssertionEvent::pxAssertionEvent( const wxString& content, const wxString& trace, SynchronousActionState* instdata )
: BaseMessageBoxEvent( content, instdata )
, m_Stacktrace( trace )
{
}
@ -169,12 +130,6 @@ pxAssertionEvent::pxAssertionEvent( const pxAssertionEvent& event )
{
}
pxAssertionEvent& pxAssertionEvent::SetInstData( MsgboxEventResult& instdata )
{
_parent::SetInstData( instdata );
return *this;
}
pxAssertionEvent& pxAssertionEvent::SetStacktrace( const wxString& trace )
{
m_Stacktrace = trace;
@ -190,33 +145,27 @@ namespace Msgbox
{
int ShowModal( BaseMessageBoxEvent& evt )
{
MsgboxEventResult instdat;
evt.SetInstData( instdat );
SynchronousActionState instdat;
evt.SetSyncState( instdat );
if( wxThread::IsMain() )
{
// main thread can handle the message immediately.
wxGetApp().ProcessEvent( evt );
return instdat.return_value;
}
else
{
// Not on main thread, must post the message there for handling instead:
wxGetApp().AddPendingEvent( evt );
instdat.WaitForMe.WaitNoCancel(); // Important! disable cancellation since we're using local stack vars.
return instdat.WaitForResult(); // Important! disable cancellation since we're using local stack vars.
}
return instdat.result;
}
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.
MsgboxEventResult instdat;
pxMessageBoxEvent tevt( instdat, title, content, buttons );
wxGetApp().AddPendingEvent( tevt );
instdat.WaitForMe.WaitNoCancel(); // Important! disable cancellation since we're using local stack vars.
return instdat.result;
pxMessageBoxEvent tevt( title, content, buttons );
return ShowModal( tevt );
}
// Pops up an alert Dialog Box with a singular "OK" button.
@ -225,10 +174,7 @@ namespace Msgbox
{
MsgButtons buttons( MsgButtons().OK() );
if( wxThread::IsMain() )
pxMessageDialog( caption, text, buttons );
else
ShowModal( caption, text, buttons );
ShowModal( caption, text, buttons );
return false;
}

View File

@ -39,6 +39,8 @@ namespace Panels
//
class DirPickerPanel : public BaseApplicableConfigPanel
{
typedef BaseApplicableConfigPanel _parent;
protected:
FoldersEnum_t m_FolderId;
wxDirPickerCtrl* m_pickerCtrl;
@ -49,9 +51,6 @@ namespace Panels
DirPickerPanel( wxWindow* parent, FoldersEnum_t folderid, const wxString& dialogLabel );
virtual ~DirPickerPanel() throw() { }
void Apply();
void AppStatusEvent_OnSettingsApplied();
void Reset();
wxDirName GetPath() const;
void SetPath( const wxString& src );
@ -62,6 +61,12 @@ namespace Panels
wxWindowID GetId() const;
wxWindowID GetPanelId() const { return m_windowId; }
// Overrides!
void Apply();
void AppStatusEvent_OnSettingsApplied();
bool Enable( bool enable=true );
protected:
void Init( FoldersEnum_t folderid, const wxString& dialogLabel, bool isCompact );
@ -71,20 +76,22 @@ namespace Panels
};
// --------------------------------------------------------------------------------------
// UsermodeSelectionPanel / LanguageSelectionPanel
// DocsFolderPickerPanel / LanguageSelectionPanel
// --------------------------------------------------------------------------------------
class UsermodeSelectionPanel : public BaseApplicableConfigPanel
class DocsFolderPickerPanel : public BaseApplicableConfigPanel
{
protected:
pxRadioPanel* m_radio_UserMode;
DirPickerPanel* m_dirpicker_custom;
public:
virtual ~UsermodeSelectionPanel() throw() { }
UsermodeSelectionPanel( wxWindow* parent, bool isFirstTime = true );
virtual ~DocsFolderPickerPanel() throw() { }
DocsFolderPickerPanel( wxWindow* parent, bool isFirstTime = true );
void Apply();
void AppStatusEvent_OnSettingsApplied();
DocsModeType GetDocsMode() const;
wxWindowID GetDirPickerId() const { return m_dirpicker_custom ? m_dirpicker_custom->GetId() : 0; }
protected:

View File

@ -70,7 +70,7 @@ void Panels::DirPickerPanel::Explore_Click( wxCommandEvent &evt )
);
if( result == wxID_CANCEL ) return;
wxMkdir( path );
wxDirName(path).Mkdir();
}
pxExplore( path );
@ -108,8 +108,8 @@ void Panels::DirPickerPanel::Init( FoldersEnum_t folderid, const wxString& dialo
// The default path is invalid... What should we do here? hmm..
}
if( !wxDir::Exists( normalized ) )
wxMkdir( normalized );
//if( !wxDir::Exists( normalized ) )
// wxMkdir( normalized );
if( !isCompact )
{
@ -191,11 +191,22 @@ void Panels::DirPickerPanel::Reset()
if( m_pickerCtrl )
{
m_pickerCtrl->Enable( m_checkCtrl ? !isDefault : true );
// Important! The dirpicker panel stuff, due to however it's put together
// needs to check the enable status of this panel before setting the child
// panel's enable status.
m_pickerCtrl->Enable( IsEnabled() ? ( m_checkCtrl ? !isDefault : true ) : false );
m_pickerCtrl->SetPath( GetNormalizedConfigFolder( m_FolderId ) );
}
}
bool Panels::DirPickerPanel::Enable( bool enable )
{
m_pickerCtrl->Enable( enable ? (!m_checkCtrl || m_checkCtrl->GetValue()) : false );
return _parent::Enable( enable );
}
void Panels::DirPickerPanel::AppStatusEvent_OnSettingsApplied()
{
Reset();
@ -204,6 +215,21 @@ void Panels::DirPickerPanel::AppStatusEvent_OnSettingsApplied()
void Panels::DirPickerPanel::Apply()
{
if( !m_pickerCtrl ) return;
const wxString path( m_pickerCtrl->GetPath() );
if( !wxDir::Exists( path ) )
{
wxDialogWithHelpers dialog( NULL, _("Create folder?"), wxVERTICAL );
dialog += dialog.Heading( _("A configured folder does not exist. Should PCSX2 to create it?") );
dialog += 12;
dialog += dialog.Heading( path );
if( wxID_CANCEL == pxIssueConfirmation( dialog, MsgButtons().Custom(_("Create")).Cancel(), L"CreateNewFolder" ) )
throw Exception::CannotApplySettings( this );
}
wxDirName(path).Mkdir();
g_Conf->Folders.Set( m_FolderId, m_pickerCtrl->GetPath(), m_checkCtrl ? m_checkCtrl->GetValue() : false );
}

View File

@ -1,6 +1,6 @@
/* 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.
@ -27,9 +27,9 @@ using namespace Dialogs;
using namespace pxSizerFlags;
// --------------------------------------------------------------------------------------
// UsermodeSelectionPanel
// DocsFolderPickerPanel
// --------------------------------------------------------------------------------------
Panels::UsermodeSelectionPanel::UsermodeSelectionPanel( wxWindow* parent, bool isFirstTime )
Panels::DocsFolderPickerPanel::DocsFolderPickerPanel( wxWindow* parent, bool isFirstTime )
: BaseApplicableConfigPanel( parent, wxVERTICAL, _("Usermode Selection") )
{
const wxString usermodeExplained( pxE( ".Panels:Usermode:Explained",
@ -44,7 +44,7 @@ Panels::UsermodeSelectionPanel::UsermodeSelectionPanel( wxWindow* parent, bool i
L"This option only affects Standard Paths which are set to use the installation default value."
) );
const RadioPanelItem UsermodeOptions[] =
const RadioPanelItem UsermodeOptions[] =
{
RadioPanelItem(
_("User Documents (recommended)"),
@ -67,7 +67,7 @@ Panels::UsermodeSelectionPanel::UsermodeSelectionPanel( wxWindow* parent, bool i
m_radio_UserMode = new pxRadioPanel( this, UsermodeOptions );
m_radio_UserMode->SetPaddingHoriz( m_radio_UserMode->GetPaddingVert() + 4 );
m_radio_UserMode->Realize();
m_dirpicker_custom = new DirPickerPanel( this, FolderId_Documents, _("Select a document root for PCSX2") );
*this += Text( isFirstTime ? usermodeExplained : usermodeWarning );
@ -75,32 +75,34 @@ Panels::UsermodeSelectionPanel::UsermodeSelectionPanel( wxWindow* parent, bool i
*this += m_dirpicker_custom | pxExpand.Border( wxLEFT, StdPadding + m_radio_UserMode->GetIndentation() );
*this += 4;
AppStatusEvent_OnSettingsApplied();
Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler(UsermodeSelectionPanel::OnRadioChanged) );
Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler(DocsFolderPickerPanel::OnRadioChanged) );
}
void Panels::UsermodeSelectionPanel::Apply()
DocsModeType Panels::DocsFolderPickerPanel::GetDocsMode() const
{
return (DocsModeType) m_radio_UserMode->GetSelection();
}
void Panels::DocsFolderPickerPanel::Apply()
{
DocsFolderMode = (DocsModeType) m_radio_UserMode->GetSelection();
CustomDocumentsFolder = m_dirpicker_custom->GetPath();
}
void Panels::UsermodeSelectionPanel::AppStatusEvent_OnSettingsApplied()
void Panels::DocsFolderPickerPanel::AppStatusEvent_OnSettingsApplied()
{
if( m_radio_UserMode ) m_radio_UserMode->SetSelection( DocsFolderMode );
if( m_dirpicker_custom ) m_dirpicker_custom->Enable( DocsFolderMode == DocsFolder_Custom );
}
void Panels::UsermodeSelectionPanel::OnRadioChanged( wxCommandEvent& evt )
void Panels::DocsFolderPickerPanel::OnRadioChanged( wxCommandEvent& evt )
{
evt.Skip();
if( !m_radio_UserMode ) return;
if( m_dirpicker_custom )
m_dirpicker_custom->Enable( m_radio_UserMode->GetSelection() == (int)DocsFolder_Custom );
m_dirpicker_custom->Enable( m_radio_UserMode->GetSelection() == (int)DocsFolder_Custom );
}
// --------------------------------------------------------------------------------------

View File

@ -39,11 +39,13 @@ BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE(pxEVT_EnumeratedNext, -1)
DECLARE_EVENT_TYPE(pxEVT_EnumerationFinished, -1)
DECLARE_EVENT_TYPE(pxEVT_ShowStatusBar, -1)
DECLARE_EVENT_TYPE(pxEvt_SysExecEventComplete, -1)
END_DECLARE_EVENT_TYPES()
DEFINE_EVENT_TYPE(pxEVT_EnumeratedNext)
DEFINE_EVENT_TYPE(pxEVT_EnumerationFinished);
DEFINE_EVENT_TYPE(pxEVT_ShowStatusBar);
DEFINE_EVENT_TYPE(pxEvt_SysExecEventComplete)
typedef s32 (CALLBACK* TestFnptr)();
typedef void (CALLBACK* ConfigureFnptr)();
@ -60,7 +62,6 @@ namespace Exception
// --------------------------------------------------------------------------------------
// PluginEnumerator class
// --------------------------------------------------------------------------------------
class PluginEnumerator
{
protected:
@ -134,10 +135,162 @@ public:
}
};
// --------------------------------------------------------------------------------------
// ApplyPluginsDialog
// --------------------------------------------------------------------------------------
class ApplyPluginsDialog : public wxDialogWithHelpers
{
DECLARE_DYNAMIC_CLASS_NO_COPY(ApplyPluginsDialog)
typedef wxDialogWithHelpers _parent;
protected:
BaseApplicableConfigPanel* m_panel;
ScopedPtr<BaseException> m_Exception;
SynchronousActionState m_sync;
public:
ApplyPluginsDialog( BaseApplicableConfigPanel* panel=NULL );
virtual ~ApplyPluginsDialog() throw() {}
virtual void RethrowException();
virtual int ShowModal();
BaseApplicableConfigPanel* GetApplicableConfigPanel() const { return m_panel; }
protected:
void OnSysExecComplete( wxCommandEvent& evt );
};
// --------------------------------------------------------------------------------------
// ApplyOverValidStateEvent
// --------------------------------------------------------------------------------------
class ApplyOverValidStateEvent : public pxInvokeActionEvent
{
//DeclareNoncopyableObject( ApplyOverValidStateEvent );
typedef pxInvokeActionEvent _parent;
protected:
ApplyPluginsDialog* m_owner;
public:
ApplyOverValidStateEvent( ApplyPluginsDialog* owner=NULL )
{
m_owner = owner;
}
virtual ~ApplyOverValidStateEvent() throw() { }
virtual ApplyOverValidStateEvent *Clone() const { return new ApplyOverValidStateEvent(*this); }
protected:
void _DoInvoke();
};
// --------------------------------------------------------------------------------------
// SysExecEvent_ApplyPlugins
// --------------------------------------------------------------------------------------
class SysExecEvent_ApplyPlugins : public SysExecEvent
{
protected:
ApplyPluginsDialog* m_dialog;
public:
virtual ~SysExecEvent_ApplyPlugins() throw() {}
SysExecEvent_ApplyPlugins* Clone() const { return new SysExecEvent_ApplyPlugins( *this ); }
SysExecEvent_ApplyPlugins( ApplyPluginsDialog* parent, SynchronousActionState& sync )
: SysExecEvent( &sync )
{
m_dialog = parent;
}
protected:
void _DoInvoke();
};
IMPLEMENT_DYNAMIC_CLASS(ApplyPluginsDialog, wxDialogWithHelpers)
ApplyPluginsDialog::ApplyPluginsDialog( BaseApplicableConfigPanel* panel )
: wxDialogWithHelpers( NULL, _("Applying settings..."), wxVERTICAL )
{
Connect( pxEvt_SysExecEventComplete, wxCommandEventHandler(ApplyPluginsDialog::OnSysExecComplete) );
GetSysExecutorThread().PostEvent( new SysExecEvent_ApplyPlugins( this, m_sync ) );
}
void ApplyOverValidStateEvent::_DoInvoke()
{
wxDialogWithHelpers dialog( m_owner, _("Shutdown PS2 virtual machine?"), wxVERTICAL );
dialog += dialog.Heading( pxE( ".Popup:PluginSelector:ConfirmShutdown",
L"Warning! Changing plugins requires a complete shutdown and reset of the PS2 virtual machine. "
L"PCSX2 will attempt to save and restore the state, but if the newly selected plugins are "
L"incompatible the recovery may fail, and current progress will be lost."
L"\n\n"
L"Are you sure you want to apply settings now?"
) );
int result = pxIssueConfirmation( dialog, MsgButtons().OK().Cancel(), L"PluginSelector:ConfirmShutdown" );
if( result == wxID_CANCEL )
throw Exception::CannotApplySettings( m_owner->GetApplicableConfigPanel(), "Cannot apply settings: canceled by user because plugins changed while the emulation state was active.", false );
}
void SysExecEvent_ApplyPlugins::_DoInvoke()
{
ScopedCoreThreadPause paused_core;
if( SysHasValidState() )
{
paused_core.AllowResume();
wxGetApp().ProcessEvent( ApplyOverValidStateEvent( m_dialog ) );
paused_core.DisallowResume();
// FIXME : We only actually have to save plugins here, except the recovery code
// in SysCoreThread isn't quite set up yet to handle that (I think...) --air
memSavingState( StateCopy_GetBuffer() ).FreezeAll();
}
ScopedCoreThreadClose closed_core;
CorePlugins.Shutdown();
CorePlugins.Unload();
LoadPluginsImmediate();
wxCommandEvent tevt( pxEvt_SysExecEventComplete );
m_dialog->GetEventHandler()->AddPendingEvent( tevt );
closed_core.AllowResume();
paused_core.AllowResume();
}
void ApplyPluginsDialog::OnSysExecComplete( wxCommandEvent& evt )
{
evt.Skip();
m_sync.WaitForResult();
EndModal( wxID_OK );
}
void ApplyPluginsDialog::RethrowException()
{
if( m_Exception ) m_Exception->Rethrow();
}
int ApplyPluginsDialog::ShowModal()
{
int result = _parent::ShowModal();
RethrowException();
return result;
}
static const wxString failed_separator( L"-------- Unsupported Plugins --------" );
// --------------------------------------------------------------------------------------
// PluginSelectorPanel implementations
// PluginSelectorPanel::StatusPanel implementations
// --------------------------------------------------------------------------------------
Panels::PluginSelectorPanel::StatusPanel::StatusPanel( wxWindow* parent )
@ -176,9 +329,9 @@ void Panels::PluginSelectorPanel::StatusPanel::Reset()
// Id for all Configure buttons (any non-negative arbitrary integer will do)
static const int ButtonId_Configure = 51;
// =====================================================================================================
// PluginSelectorPanel::ComboBoxPanel
// =====================================================================================================
// --------------------------------------------------------------------------------------
// PluginSelectorPanel::ComboBoxPanel implementations
// --------------------------------------------------------------------------------------
Panels::PluginSelectorPanel::ComboBoxPanel::ComboBoxPanel( PluginSelectorPanel* parent )
: wxPanelWithHelpers( parent, wxVERTICAL )
, m_FolderPicker( *new DirPickerPanel( this, FolderId_Plugins,
@ -224,9 +377,6 @@ void Panels::PluginSelectorPanel::ComboBoxPanel::Reset()
}
}
// =====================================================================================================
// PluginSelectorPanel
// =====================================================================================================
void Panels::PluginSelectorPanel::DispatchEvent( const PluginEventType& evt )
{
if( (evt != CorePlugins_Loaded) && (evt != CorePlugins_Unloaded) ) return; // everything else we don't care about
@ -337,64 +487,47 @@ void Panels::PluginSelectorPanel::Apply()
break;
} while( ++pi, pi->shortname != NULL );
bool isSuspended = false;
if( pi->shortname == NULL ) return; // no plugins changed? nothing left to do!
if( pi->shortname != NULL )
// ----------------------------------------------------------------------------
// Plugin names are not up-to-date -- RELOAD!
ApplyPluginsDialog applyDlg( this );
wxBoxSizer& paddedMsg( *new wxBoxSizer( wxHORIZONTAL ) );
paddedMsg += 24;
paddedMsg += applyDlg.Heading(_("Applying Settings..."));
paddedMsg += 24;
applyDlg += 12;
applyDlg += paddedMsg;
applyDlg += 12;
applyDlg += new wxButton( &applyDlg, wxID_CANCEL ) | pxCenter;
applyDlg += 6;
//applyDlg.GetEventHandler()->AddPendingEvent( );
try
{
if( CoreThread.IsRunning() )
{
// [TODO] : Post notice that this shuts down existing emulation, and may not safely recover.
wxDialogWithHelpers dialog( this, _("Shutdown PS2 virtual machine?"), wxVERTICAL );
dialog += dialog.Heading( pxE( ".Popup:PluginSelector:ConfirmShutdown",
L"Warning! Changing plugins requires a complete shutdown and reset of the PS2 virtual machine. "
L"PCSX2 will attempt to save and restore the state, but if the newly selected plugins are "
L"incompatible the recovery may fail, and current progress will be lost."
L"\n\n"
L"Are you sure you want to apply settings now?"
) );
int result = pxIssueConfirmation( dialog, MsgButtons().OK().Cancel(), L"PluginSelector:ConfirmShutdown" );
if( result == wxID_CANCEL )
throw Exception::CannotApplySettings( this, "Cannot apply settings: canceled by user because plugins changed while the emulation state was active.", false );
// FIXME : We only actually have to save plugins here, except the recovery code
// in SysCoreThread isn't quite set up yet to handle that (I think...) --air
isSuspended = CoreThread.Suspend();
StateCopy_FreezeToMem_Blocking();
}
// Don't use SysShutdown, it clears the StateCopy.
CoreThread.Cancel();
wxGetApp().m_CorePlugins = NULL;
if( wxID_CANCEL == applyDlg.ShowModal() )
throw Exception::CannotApplySettings( this, "User canceled plugin load process.", false );
}
if( !wxGetApp().m_CorePlugins )
catch( Exception::PluginError& ex )
{
try {
LoadPluginsImmediate();
}
catch( Exception::PluginError& ex )
{
// Rethrow PluginLoadErrors as a failure to Apply...
// Rethrow PluginLoadErrors as a failure to Apply...
wxString plugname( tbl_PluginInfo[ex.PluginId].GetShortname() );
wxString plugname( tbl_PluginInfo[ex.PluginId].GetShortname() );
throw Exception::CannotApplySettings( this,
// Diagnostic
ex.FormatDiagnosticMessage(),
throw Exception::CannotApplySettings( this,
// Diagnostic
ex.FormatDiagnosticMessage(),
// Translated
wxsFormat( _("The selected %s plugin failed to load.\n\nReason: %s\n\n"),
plugname.c_str(), ex.FormatDisplayMessage().c_str()
) + GetApplyFailedMsg()
);
}
// Translated
wxsFormat( _("The selected %s plugin failed to load.\n\nReason: %s\n\n"),
plugname.c_str(), ex.FormatDisplayMessage().c_str()
) + GetApplyFailedMsg()
);
}
if( isSuspended ) CoreThread.Resume();
}
void Panels::PluginSelectorPanel::CancelRefresh()
@ -480,9 +613,9 @@ void Panels::PluginSelectorPanel::OnPluginSelected( wxCommandEvent& evt )
// (a) plugins aren't even loaded yet.
// (b) current selection matches exactly the currently configured/loaded plugin.
bool isSame = (g_plugins==NULL) || g_Conf->FullpathMatchTest( pi->id, (*m_FileList)[(int)box.GetClientData(box.GetSelection())] );
bool isSame = (!CorePlugins.AreLoaded()) || g_Conf->FullpathMatchTest( pi->id, (*m_FileList)[(int)box.GetClientData(box.GetSelection())] );
m_ComponentBoxes->GetConfigButton( pi->id ).Enable( isSame );
if( !isSame ) evt.Skip(); // enabled Apply button! :D
return;
}
@ -504,7 +637,7 @@ void Panels::PluginSelectorPanel::OnConfigure_Clicked( wxCommandEvent& evt )
const wxString filename( (*m_FileList)[(int)m_ComponentBoxes->Get(pid).GetClientData(sel)] );
if( g_plugins != NULL && !g_Conf->FullpathMatchTest( pid, filename ) )
if( CorePlugins.AreLoaded() && !g_Conf->FullpathMatchTest( pid, filename ) )
{
Console.Warning( "(PluginSelector) Plugin name mismatch, configuration request ignored." );
return;
@ -544,7 +677,7 @@ void Panels::PluginSelectorPanel::OnEnumComplete( wxCommandEvent& evt )
else if( m_ComponentBoxes->Get(pid).GetSelection() == wxNOT_FOUND )
{
m_ComponentBoxes->Get(pid).SetSelection( 0 );
m_ComponentBoxes->GetConfigButton(pid).Enable( g_plugins == NULL );
m_ComponentBoxes->GetConfigButton(pid).Enable( CorePlugins.AreLoaded() );
}
} while( ++pi, pi->shortname != NULL );

View File

@ -1,396 +0,0 @@
/* 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 "MainFrame.h"
#include <wx/dir.h>
#include <wx/file.h>
#include "Plugins.h"
#include "GS.h"
#include "HostGui.h"
#include "AppConfig.h"
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
// --------------------------------------------------------------------------------------
// This extension of PluginManager provides event listener sources for plugins -- loading,
// unloading, open, close, shutdown, etc.
//
// FIXME : Should this be made part of the PCSX2 core emulation? (integrated into PluginManager)
// I'm undecided on if it makes sense more in that context or in this one (interface).
//
class AppPluginManager : public PluginManager
{
typedef PluginManager _parent;
public:
AppPluginManager( const wxString (&folders)[PluginId_Count] )
: PluginManager( folders )
{
SetSettingsFolder( GetSettingsFolder().ToString() );
wxGetApp().PostPluginStatus( CorePlugins_Loaded );
}
virtual ~AppPluginManager() throw()
{
sApp.PostPluginStatus( CorePlugins_Unloaded );
}
void Init()
{
SetSettingsFolder( GetSettingsFolder().ToString() );
_parent::Init();
sApp.PostPluginStatus( CorePlugins_Init );
}
void Shutdown()
{
_parent::Shutdown();
sApp.PostPluginStatus( CorePlugins_Shutdown );
}
void Close()
{
if( !NeedsClose() ) return;
sApp.PostPluginStatus( CorePlugins_Closing );
_parent::Close();
sApp.PostPluginStatus( CorePlugins_Closed );
}
void Open()
{
SetSettingsFolder( GetSettingsFolder().ToString() );
if( !NeedsOpen() ) return;
sApp.PostPluginStatus( CorePlugins_Opening );
_parent::Open();
sApp.PostPluginStatus( CorePlugins_Opened );
}
// Yay, this plugin is guaranteed to always be opened first and closed last.
bool OpenPlugin_GS()
{
if( GSopen2 != NULL && !s_DisableGsWindow )
{
sApp.OpenGsPanel();
}
bool retval = _parent::OpenPlugin_GS();
if( g_LimiterMode == Limit_Turbo )
GSsetVsync( false );
return retval;
}
// Yay, this plugin is guaranteed to always be opened first and closed last.
void ClosePlugin_GS()
{
if( GSopen2 == NULL || CloseViewportWithPlugins )
{
// All other plugins must be closed before the GS, because they all rely on
// the GS window handle being valid. The recursion guard will protect this
// function from being called a million times. ;)
static int _guard;
RecursionGuard mess( _guard );
if( !mess.IsReentrant() ) Close();
}
_parent::ClosePlugin_GS();
sApp.CloseGsPanel();
}
/*void Open( PluginsEnum_t pid )
{
_parent::Open( pid );
}
void Close( PluginsEnum_t pid )
{
_parent::Close( pid );
}*/
};
// --------------------------------------------------------------------------------------
// LoadPluginsTask
// --------------------------------------------------------------------------------------
// On completion the thread sends a pxEvt_LoadPluginsComplete message, which contains a
// handle to this thread object. If the load is successful, the Result var is set to
// non-NULL. If NULL, an error occurred and the thread loads the exception into either
// Ex_PluginError or Ex_RuntimeError.
//
class LoadPluginsTask : public PersistentThread
{
typedef PersistentThread _parent;
public:
PluginManager* Result;
protected:
wxString m_folders[PluginId_Count];
ScopedBusyCursor m_hourglass;
public:
LoadPluginsTask( const wxString (&folders)[PluginId_Count] ) : Result( NULL ), m_hourglass( Cursor_KindaBusy )
{
for(int i=0; i<PluginId_Count; ++i )
m_folders[i] = folders[i];
}
virtual ~LoadPluginsTask() throw();
protected:
void OnCleanupInThread();
void ExecuteTaskInThread();
};
LoadPluginsTask::~LoadPluginsTask() throw()
{
PersistentThread::Cancel();
}
void LoadPluginsTask::ExecuteTaskInThread()
{
wxGetApp().Ping();
// This is for testing of the error handler... uncomment for fun?
//throw Exception::PluginError( PluginId_PAD, "This one is for testing the error handler!" );
Result = new AppPluginManager( m_folders );
}
void LoadPluginsTask::OnCleanupInThread()
{
wxGetApp().PostCommand( this, pxEvt_LoadPluginsComplete );
_parent::OnCleanupInThread();
}
// --------------------------------------------------------------------------------------
// SaveSinglePluginHelper (Implementations)
// --------------------------------------------------------------------------------------
SaveSinglePluginHelper::SaveSinglePluginHelper( PluginsEnum_t pid )
: m_plugstore( L"PluginConf Savestate" )
{
s_DisableGsWindow = true;
m_whereitsat = NULL;
m_resume = false;
m_pid = pid;
m_validstate = SysHasValidState();
if( !m_validstate ) return;
Console.WriteLn( Color_Green, L"Suspending single plugin: " + tbl_PluginInfo[m_pid].GetShortname() );
m_resume = CoreThread.Pause();
m_whereitsat = StateCopy_GetBuffer();
if( m_whereitsat == NULL )
{
m_whereitsat = &m_plugstore;
memSavingState save( m_plugstore );
g_plugins->Freeze( m_pid, save );
}
g_plugins->Close( pid );
}
SaveSinglePluginHelper::~SaveSinglePluginHelper() throw()
{
try
{
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 );
}
s_DisableGsWindow = false;
if( m_resume ) CoreThread.Resume();
}
DESTRUCTOR_CATCHALL;
s_DisableGsWindow = false;
}
/////////////////////////////////////////////////////////////////////////////////////////
int EnumeratePluginsInFolder( const wxDirName& searchpath, wxArrayString* dest )
{
ScopedPtr<wxArrayString> placebo;
wxArrayString* realdest = dest;
if( realdest == NULL )
placebo = realdest = new wxArrayString(); // placebo is our /dev/null -- gets deleted when done
#ifdef __WXMSW__
// Windows pretty well has a strict "must end in .dll" rule.
wxString pattern( L"*%s" );
#else
// Other platforms seem to like to version their libs after the .so extension:
// blah.so.3.1.fail?
wxString pattern( L"*%s*" );
#endif
return searchpath.Exists() ?
wxDir::GetAllFiles( searchpath.ToString(), realdest, wxsFormat( pattern, wxDynamicLibrary::GetDllExt()), wxDIR_FILES ) : 0;
}
void ConvertPluginFilenames( wxString (&passins)[PluginId_Count] )
{
const PluginInfo* pi = tbl_PluginInfo; do
{
passins[pi->id] = OverrideOptions.Filenames[pi->id].GetFullPath();
if( passins[pi->id].IsEmpty() || !wxFileExists( passins[pi->id] ) )
passins[pi->id] = g_Conf->FullpathTo( pi->id );
} while( ++pi, pi->shortname != NULL );
}
// boolean lock modified from the main thread only...
static LoadPluginsTask* plugin_load_lock = NULL;
void Pcsx2App::ReloadPlugins()
{
if( InvokeMethodOnMainThread( &Pcsx2App::ReloadPlugins ) ) return;
if( plugin_load_lock ) return;
UnloadPlugins();
wxString passins[PluginId_Count];
const PluginInfo* pi = tbl_PluginInfo; do
{
passins[pi->id] = OverrideOptions.Filenames[pi->id].GetFullPath();
if( passins[pi->id].IsEmpty() || !wxFileExists( passins[pi->id] ) )
passins[pi->id] = g_Conf->FullpathTo( pi->id );
} while( ++pi, pi->shortname != NULL );
plugin_load_lock = new LoadPluginsTask(passins);
plugin_load_lock->Start();
// ... and when it finishes it posts up a OnLoadPluginsComplete(). Bye. :)
}
// Note: If the ClientData parameter of wxCommandEvt is NULL, this message simply dispatches
// the plugged in listeners.
void Pcsx2App::OnLoadPluginsComplete( wxCommandEvent& evt )
{
FnType_OnThreadComplete* fn_tmp = Callback_PluginsLoadComplete;
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;
killTask->RethrowException();
m_CorePlugins = killTask->Result;
}
if( fn_tmp != NULL ) fn_tmp( evt );
}
void Pcsx2App::CancelLoadingPlugins()
{
if( plugin_load_lock )
plugin_load_lock->Cancel();
}
void Pcsx2App::PostPluginStatus( PluginEventType pevt )
{
if( !wxThread::IsMain() )
{
PostCommand( pxEvt_PluginStatus, pevt );
}
else
{
sApp.DispatchEvent( pevt );
}
}
void Pcsx2App::OnPluginStatus( wxCommandEvent& evt )
{
PostPluginStatus( (PluginEventType)evt.GetInt() );
}
// Posts a message to the App to reload plugins. Plugins are loaded via a background thread
// which is started on a pending event, so don't expect them to be ready "right now."
// If plugins are already loaded, onComplete is invoked, and the function returns with no
// other actions performed.
void LoadPluginsPassive( FnType_OnThreadComplete* onComplete )
{
// Plugins already loaded?
if( wxGetApp().m_CorePlugins )
{
if( onComplete ) onComplete( wxCommandEvent( pxEvt_LoadPluginsComplete ) );
return;
}
if( onComplete )
Callback_PluginsLoadComplete = onComplete;
wxGetApp().ReloadPlugins();
}
// Performs a blocking load of plugins. If the emulation thread is active, it is shut down
// automatically to prevent race conditions (it depends on plugins).
//
// Exceptions regarding plugin failures will propagate out of this function, so be prepared
// to handle them.
//
// Note that this is not recommended for most situations, but coding improper passive loads
// is probably worse, so if in doubt use this and air will fix it up for you later. :)
//
void LoadPluginsImmediate()
{
if( g_plugins != NULL ) return;
CoreThread.Cancel();
wxString passins[PluginId_Count];
ConvertPluginFilenames( passins );
wxGetApp().m_CorePlugins = new AppPluginManager( passins );
}
void UnloadPlugins()
{
CoreThread.Cancel();
sApp.m_CorePlugins = NULL;
}

View File

@ -46,7 +46,7 @@ void RecentIsoManager::OnChangedSelection( wxCommandEvent& evt )
{
if( (m_Items[i].ItemPtr != NULL) && (m_Items[i].ItemPtr->GetId() == evt.GetId()) ) break;
}
if( i >= m_Items.size() )
{
evt.Skip();
@ -55,9 +55,11 @@ void RecentIsoManager::OnChangedSelection( wxCommandEvent& evt )
m_cursel = i;
bool resume = CoreThread.Suspend();
// TODO: Dialog asking for hotswap or reset!!!!
ScopedCoreThreadClose stopped_core;
SysUpdateIsoSrcFile( m_Items[i].Filename );
if( resume ) CoreThread.Resume();
stopped_core.AllowResume();
}
void RecentIsoManager::RemoveAllFromMenu()
@ -72,7 +74,7 @@ void RecentIsoManager::RemoveAllFromMenu()
m_Menu->Destroy( curitem.ItemPtr );
curitem.ItemPtr = NULL;
}
if( m_Separator != NULL )
{
m_Menu->Destroy( m_Separator );
@ -93,7 +95,7 @@ void RecentIsoManager::Repopulate()
if( cnt <= 0 ) return;
m_Separator = m_Menu->AppendSeparator();
for( int i=0; i<cnt; ++i )
InsertIntoMenu( i );
}
@ -172,7 +174,7 @@ void RecentIsoManager::AppStatusEvent_OnSettingsLoadSave( const AppSettingsEvent
else
{
RemoveAllFromMenu();
m_MaxLength = g_Conf->RecentIsoCount;
IniScopedGroup groupie( ini, L"RecentIso" );
for( uint i=0; i<m_MaxLength; ++i )

310
pcsx2/gui/SysState.cpp Normal file
View File

@ -0,0 +1,310 @@
/* 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 te 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 "SysThreads.h"
#include "SaveState.h"
#include "ZipTools\ThreadedZipTools.h"
// Used to hold the current state backup (fullcopy of PS2 memory and plugin states).
static SafeArray<u8> state_buffer( L"Public Savestate Buffer" );
static const char SavestateIdentString[] = "PCSX2 Savestate";
static const uint SavestateIdentLen = sizeof(SavestateIdentString);
static void SaveStateFile_WriteHeader( IStreamWriter& thr )
{
thr.Write( SavestateIdentString );
thr.Write( g_SaveVersion );
}
static void SaveStateFile_ReadHeader( IStreamReader& thr )
{
char ident[SavestateIdentLen] = {0};
thr.Read( ident );
if( strcmp(SavestateIdentString, ident) )
throw Exception::SaveStateLoadError( thr.GetStreamName(),
wxsFormat( L"Unrecognized file signature while loading savestate: %s", ident ),
_("File is not a valid PCSX2 savestate, or is from an older unsupported version of PCSX2.")
);
u32 savever;
thr.Read( savever );
if( (savever >> 16) != (g_SaveVersion >> 16) )
throw Exception::SaveStateLoadError( thr.GetStreamName(),
wxsFormat( L"Unrecognized file signature while loading savestate: %s", ident ),
_("File is not a valid PCSX2 savestate, or is from an older unsupported version of PCSX2.")
);
if( savever > g_SaveVersion )
throw Exception::SaveStateLoadError( thr.GetStreamName(),
wxsFormat( L"Unrecognized file signature while loading savestate: %s", ident ),
_("File is not a valid PCSX2 savestate, or is from an older unsupported version of PCSX2.")
);
};
// --------------------------------------------------------------------------------------
// gzipReader
// --------------------------------------------------------------------------------------
// Interface for reading data from a gzip stream.
//
class gzipReader : public IStreamReader
{
protected:
wxString m_filename;
gzFile m_gzfp;
public:
gzipReader( const wxString& filename )
: m_filename( filename )
{
if( NULL == (m_gzfp = gzopen( m_filename.ToUTF8(), "rb" )) )
throw Exception::CannotCreateStream( m_filename, "Cannot open file for reading." );
gzbuffer(m_gzfp, 0x100000); // 1mb buffer for zlib internal operations
}
virtual ~gzipReader() throw ()
{
if( m_gzfp ) gzclose( m_gzfp );
}
wxString GetStreamName() const { return m_filename; }
void Read( void* dest, size_t size )
{
int result = gzread( m_gzfp, dest, size );
if( result == -1)
throw Exception::BadStream( m_filename, "Data read failed: Invalid or corrupted gzip archive." );
if( (size_t)result < size )
throw Exception::EndOfStream( m_filename );
}
};
// --------------------------------------------------------------------------------------
// SysExecEvent_DownloadState
// --------------------------------------------------------------------------------------
// Pauses core emulation and downloads the savestate into the state_buffer.
//
class SysExecEvent_DownloadState : public SysExecEvent
{
protected:
SafeArray<u8>* m_dest_buffer;
public:
virtual ~SysExecEvent_DownloadState() throw() {}
SysExecEvent_DownloadState* Clone() const { return new SysExecEvent_DownloadState( *this ); }
SysExecEvent_DownloadState( SafeArray<u8>* dest=&state_buffer )
{
m_dest_buffer = dest;
}
bool AllowCancelOnExit() const { return false; }
protected:
void _DoInvoke()
{
ScopedCoreThreadPause paused_core;
if( !SysHasValidState() )
throw Exception::RuntimeError( L"Cannot complete state freeze request; the virtual machine state is reset.", _("You'll need to start a new virtual machine before you can save its state.") );
memSavingState(m_dest_buffer).FreezeAll();
paused_core.AllowResume();
}
};
// --------------------------------------------------------------------------------------
// SysExecEvent_ZipToDisk
// --------------------------------------------------------------------------------------
class SysExecEvent_ZipToDisk : public SysExecEvent
{
protected:
SafeArray<u8>* m_src_buffer;
wxString m_filename;
public:
wxString GetEventName() const { return L"SysState_ZipToDisk"; }
virtual ~SysExecEvent_ZipToDisk() throw()
{
delete m_src_buffer;
}
SysExecEvent_ZipToDisk* Clone() const { return new SysExecEvent_ZipToDisk( *this ); }
SysExecEvent_ZipToDisk( ScopedPtr<SafeArray<u8>>& src, const wxString& filename )
: m_filename( filename )
{
m_src_buffer = src.DetachPtr();
}
SysExecEvent_ZipToDisk( SafeArray<u8>* src, const wxString& filename )
: m_filename( filename )
{
m_src_buffer = src;
}
bool IsCriticalEvent() const { return true; }
bool AllowCancelOnExit() const { return false; }
protected:
void _DoInvoke()
{
(new CompressThread_gzip( m_filename, m_src_buffer, SaveStateFile_WriteHeader ))->Start();
m_src_buffer = NULL;
}
};
// --------------------------------------------------------------------------------------
// SysExecEvent_UnzipFromDisk
// --------------------------------------------------------------------------------------
// Note: Unzipping always goes directly into the state_buffer, and is always a blocking
// action on the SysExecutor thread (the system cannot execute other commands while states
// are unzipping or uplading into the system).
//
class SysExecEvent_UnzipFromDisk : public SysExecEvent
{
protected:
wxString m_filename;
gzipReader m_gzreader;
public:
wxString GetEventName() const { return L"SysState_UnzipFromDisk"; }
virtual ~SysExecEvent_UnzipFromDisk() throw() {}
SysExecEvent_UnzipFromDisk* Clone() const { return new SysExecEvent_UnzipFromDisk( *this ); }
SysExecEvent_UnzipFromDisk( const wxString& filename )
: m_filename( filename )
, m_gzreader( filename )
{
SaveStateFile_ReadHeader( m_gzreader );
}
wxString GetStreamName() const { return m_filename; }
protected:
void _DoInvoke()
{
// We use direct Suspend/Resume control here, since it's desirable that emulation
// *ALWAYS* start execution after the new savestate is loaded.
GetCoreThread().Pause();
// fixme: should start initially with the file size, and then grow from there.
static const int BlockSize = 0x100000;
state_buffer.MakeRoomFor( 0x800000 ); // start with an 8 meg buffer to avoid frequent reallocation.
int curidx = 0;
try {
while(true) {
state_buffer.MakeRoomFor( curidx+BlockSize );
m_gzreader.Read( state_buffer.GetPtr(curidx), BlockSize );
curidx += BlockSize;
Threading::pxTestCancel();
}
}
catch( Exception::EndOfStream& )
{
// This exception actually means success! Any others we let get sent
// to the main event handler/thread for handling.
}
// Optional shutdown of plugins when loading states? I'm not implementing it yet because some
// things, like the SPU2-recovery trick, rely on not resetting the plugins prior to loading
// the new savestate data.
//if( ShutdownOnStateLoad ) GetCoreThread().Cancel();
GetCoreThread().RecoverState();
GetCoreThread().Resume(); // force resume regardless of emulation state earlier.
}
};
// =====================================================================================================
// StateCopy Public Interface
// =====================================================================================================
SafeArray<u8>& StateCopy_GetBuffer()
{
return state_buffer;
}
bool StateCopy_IsValid()
{
return !state_buffer.IsDisposed();
}
void StateCopy_FreezeToMem()
{
GetSysExecutorThread().PostEvent( new SysExecEvent_DownloadState() );
}
void StateCopy_SaveToFile( const wxString& file )
{
ScopedPtr<SafeArray<u8>> zipbuf(new SafeArray<u8>( L"Zippable Savestate" ));
GetSysExecutorThread().PostEvent(new SysExecEvent_DownloadState( zipbuf ));
GetSysExecutorThread().PostEvent(new SysExecEvent_ZipToDisk( zipbuf, file ));
}
void StateCopy_LoadFromFile( const wxString& file )
{
UI_DisableSysActions();
GetSysExecutorThread().PostEvent(new SysExecEvent_UnzipFromDisk( file ));
}
// Saves recovery state info to the given saveslot, or saves the active emulation state
// (if one exists) and no recovery data was found. This is needed because when a recovery
// state is made, the emulation state is usually reset so the only persisting state is
// the one in the memory save. :)
void StateCopy_SaveToSlot( uint num )
{
const wxString file( SaveStateBase::GetFilename( num ) );
Console.WriteLn( Color_StrongGreen, "Saving savestate to slot %d...", num );
Console.Indent().WriteLn( Color_StrongGreen, L"filename: %s", file.c_str() );
StateCopy_SaveToFile( file );
}
void StateCopy_LoadFromSlot( uint slot )
{
wxString file( SaveStateBase::GetFilename( slot ) );
if( !wxFileExists( file ) )
{
Console.Warning( "Savestate slot %d is empty.", slot );
return;
}
Console.WriteLn( Color_StrongGreen, "Loading savestate from slot %d...", slot );
Console.Indent().WriteLn( Color_StrongGreen, L"filename: %s", file.c_str() );
StateCopy_LoadFromFile( file );
}
void StateCopy_Clear()
{
state_buffer.Dispose();
}

80
pcsx2/gui/UpdateUI.cpp Normal file
View File

@ -0,0 +1,80 @@
/* 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 "Mainframe.h"
#include "GSFrame.h"
static void _SaveLoadStuff( bool enabled )
{
sMainFrame.GetMenuBar()->Enable( MenuId_Sys_LoadStates, enabled );
sMainFrame.GetMenuBar()->Enable( MenuId_Sys_SaveStates, enabled );
}
// Updates the enable/disable status of all System related controls: menus, toolbars,
// etc. Typically called by SysEvtHandler whenever the message pump becomes idle.
void UI_UpdateSysControls()
{
if( wxGetApp().PostMethodMyself( &UI_UpdateSysControls ) ) return;
sApp.PostAction( CoreThreadStatusEvent( CoreThread_Indeterminate ) );
_SaveLoadStuff( true );
}
void UI_DisableSysReset()
{
if( wxGetApp().PostMethodMyself( UI_DisableSysReset ) ) return;
sMainFrame.GetMenuBar()->Enable( MenuId_Sys_Restart, false );
_SaveLoadStuff( false );
}
void UI_DisableSysShutdown()
{
if( wxGetApp().PostMethodMyself( &UI_DisableSysShutdown ) ) return;
sMainFrame.GetMenuBar()->Enable( MenuId_Sys_Restart, false );
sMainFrame.GetMenuBar()->Enable( MenuId_Sys_Shutdown, false );
}
void UI_EnableSysShutdown()
{
if( wxGetApp().PostMethodMyself( &UI_EnableSysShutdown ) ) return;
sMainFrame.GetMenuBar()->Enable( MenuId_Sys_Restart, true );
sMainFrame.GetMenuBar()->Enable( MenuId_Sys_Shutdown, true );
}
void UI_DisableSysActions()
{
if( wxGetApp().PostMethodMyself( &UI_DisableSysShutdown ) ) return;
sMainFrame.GetMenuBar()->Enable( MenuId_Sys_Restart, false );
sMainFrame.GetMenuBar()->Enable( MenuId_Sys_Shutdown, false );
}
void UI_EnableSysActions()
{
if( wxGetApp().PostMethodMyself( &UI_EnableSysShutdown ) ) return;
sMainFrame.GetMenuBar()->Enable( MenuId_Sys_Restart, true );
sMainFrame.GetMenuBar()->Enable( MenuId_Sys_Shutdown, true );
}

188
pcsx2/gui/pxEventThread.h Normal file
View File

@ -0,0 +1,188 @@
/* 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/PersistentThread.h"
#include "Utilities/pxEvents.h"
// TODO!! Make this system a bit more generic, and then move it to the Utilities library.
// --------------------------------------------------------------------------------------
// SysExecEvent
// --------------------------------------------------------------------------------------
class SysExecEvent
: public IActionInvocation
, public ICloneable
{
protected:
SynchronousActionState* m_sync;
public:
virtual ~SysExecEvent() throw() {}
SysExecEvent* Clone() const { return new SysExecEvent( *this ); }
SysExecEvent( SynchronousActionState* sync=NULL )
{
m_sync = sync;
}
SysExecEvent( SynchronousActionState& sync )
{
m_sync = &sync;
}
const SynchronousActionState* GetSyncState() const { return m_sync; }
SynchronousActionState* GetSyncState() { return m_sync; }
void SetSyncState( SynchronousActionState* obj ) { m_sync = obj; }
void SetSyncState( SynchronousActionState& obj ) { m_sync = &obj; }
// Tells the Event Handler whether or not this event can be skipped when the system
// is being quit or reset. Typically set this to true for events which shut down the
// system, since program crashes can occur if the program tries to exit while threads
// are running.
virtual bool IsCriticalEvent() const { return false; }
// Tells the Event Handler whether or not this event can be canceled. Typically events
// should not prohibit cancellation, since it expedites program termination and helps
// avoid permanent deadlock. Some actions like saving states and shutdown procedures
// should not allow cancellation since they could result in program crashes or corrupted
// data.
virtual bool AllowCancelOnExit() const { return true; }
virtual void InvokeAction();
virtual void PostResult() const;
virtual wxString GetEventName() const { return wxEmptyString; }
virtual wxString GetEventMessage() const { return wxEmptyString; }
virtual int GetResult()
{
if( !pxAssertDev( m_sync != NULL, "SysEvent: Expecting return value, but no sync object provided." ) ) return 0;
return m_sync->return_value;
}
virtual void SetException( BaseException* ex );
void SetException( const BaseException& ex );
protected:
virtual void _DoInvoke();
};
// --------------------------------------------------------------------------------------
// SysExecEvent_Method
// --------------------------------------------------------------------------------------
class SysExecEvent_Method : public SysExecEvent
{
protected:
FnType_Void* m_method;
public:
virtual ~SysExecEvent_Method() throw() {}
SysExecEvent_Method* Clone() const { return new SysExecEvent_Method( *this ); }
explicit SysExecEvent_Method( FnType_Void* method = NULL )
{
m_method = method;
}
protected:
void _DoInvoke()
{
if( m_method ) m_method();
}
};
typedef std::list<SysExecEvent*> pxEvtList;
// --------------------------------------------------------------------------------------
// pxEvtHandler
// --------------------------------------------------------------------------------------
// wxWidgets Event Queue (wxEvtHandler) isn't thread-safe (uses static vars and checks/modifies wxApp globals
// while processing), so it's useless to us. Have to roll our own. -_-
//
class pxEvtHandler
{
protected:
pxEvtList m_pendingEvents;
Threading::MutexRecursive m_mtx_pending;
Threading::Semaphore m_wakeup;
wxThreadIdType m_OwnerThreadId;
volatile u32 m_Quitting;
public:
pxEvtHandler();
virtual ~pxEvtHandler() throw() {}
virtual wxString GetEventHandlerName() const { return L"pxEvtHandler"; }
virtual void ShutdownQueue();
bool IsShuttingDown() const { return !!m_Quitting; }
void ProcessPendingEvents();
void AddPendingEvent( SysExecEvent& evt );
void PostEvent( SysExecEvent* evt );
void PostEvent( const SysExecEvent& evt );
void ProcessEvent( SysExecEvent* evt );
void ProcessEvent( SysExecEvent& evt );
bool SelfProcessMethod( FnType_Void* method );
void Idle();
void SetActiveThread();
protected:
virtual void DoIdle() {}
};
// --------------------------------------------------------------------------------------
// ExecutorThread
// --------------------------------------------------------------------------------------
class ExecutorThread : public Threading::PersistentThread
{
typedef Threading::PersistentThread _parent;
protected:
ScopedPtr<wxTimer> m_ExecutorTimer;
ScopedPtr<pxEvtHandler> m_EvtHandler;
public:
ExecutorThread( pxEvtHandler* evtandler = NULL );
virtual ~ExecutorThread() throw() { }
virtual void ShutdownQueue();
void PostEvent( SysExecEvent* evt );
void PostEvent( const SysExecEvent& evt );
void ProcessEvent( SysExecEvent* evt );
void ProcessEvent( SysExecEvent& evt );
bool SelfProcessMethod( void (*evt)() )
{
return m_EvtHandler ? m_EvtHandler->SelfProcessMethod( evt ) : false;
}
protected:
void OnStart();
void ExecuteTaskInThread();
void OnCleanupInThread();
};

View File

@ -83,7 +83,7 @@ void pxLogTextCtrl::OnThumbTrack(wxScrollWinEvent& evt)
//Console.Warning( "Thumb Tracking!!!" );
m_FreezeWrites = true;
if( !m_IsPaused )
m_IsPaused = CoreThread.Pause();
m_IsPaused = new ScopedCoreThreadPause();
evt.Skip();
}
@ -94,8 +94,8 @@ void pxLogTextCtrl::OnThumbRelease(wxScrollWinEvent& evt)
m_FreezeWrites = false;
if( m_IsPaused )
{
CoreThread.Resume();
m_IsPaused = false;
m_IsPaused->AllowResume();
m_IsPaused.Delete();
}
evt.Skip();
}

View File

@ -1880,6 +1880,10 @@
RelativePath="..\..\gui\AppConfig.cpp"
>
</File>
<File
RelativePath="..\..\gui\AppCorePlugins.cpp"
>
</File>
<File
RelativePath="..\..\gui\AppCoreThread.cpp"
>
@ -1940,6 +1944,10 @@
RelativePath="..\..\gui\CpuUsageProviderMSW.cpp"
>
</File>
<File
RelativePath="..\..\gui\ExecutorThread.cpp"
>
</File>
<File
RelativePath="..\..\gui\FrameForGS.cpp"
>
@ -1980,10 +1988,6 @@
RelativePath="..\..\gui\MSWstuff.cpp"
>
</File>
<File
RelativePath="..\..\gui\Plugins.cpp"
>
</File>
<File
RelativePath="..\..\gui\pxLogTextCtrl.cpp"
>
@ -1993,11 +1997,7 @@
>
</File>
<File
RelativePath="..\..\RecoverySystem.cpp"
>
</File>
<File
RelativePath="..\..\gui\Saveslots.cpp"
RelativePath="..\..\gui\UpdateUI.cpp"
>
</File>
<Filter
@ -2653,6 +2653,14 @@
RelativePath="..\..\gui\AppConfig.h"
>
</File>
<File
RelativePath="..\..\gui\AppCorePlugins.h"
>
</File>
<File
RelativePath="..\..\gui\AppCoreThread.h"
>
</File>
<File
RelativePath="..\..\gui\AppEventListeners.h"
>
@ -2681,6 +2689,10 @@
RelativePath="..\..\gui\Resources\EmbeddedImage.h"
>
</File>
<File
RelativePath="..\..\gui\GSFrame.h"
>
</File>
<File
RelativePath="..\..\HostGui.h"
>
@ -2697,6 +2709,10 @@
RelativePath="..\..\gui\MainFrame.h"
>
</File>
<File
RelativePath="..\..\gui\pxEventThread.h"
>
</File>
<File
RelativePath="..\..\gui\RecentIsoList.h"
>
@ -2822,6 +2838,38 @@
>
</File>
</Filter>
<Filter
Name="VM States"
>
<File
RelativePath="..\..\gui\Saveslots.cpp"
>
</File>
<File
RelativePath="..\..\gui\SysState.cpp"
>
</File>
</Filter>
<Filter
Name="ZipTools"
>
<File
RelativePath="..\..\ZipTools\FolderDesc.txt"
>
</File>
<File
RelativePath="..\..\ZipTools\thread_gzip.cpp"
>
</File>
<File
RelativePath="..\..\ZipTools\thread_lzma.cpp"
>
</File>
<File
RelativePath="..\..\ZipTools\ThreadedZipTools.h"
>
</File>
</Filter>
</Filter>
</Files>
<Globals>

View File

@ -32,10 +32,10 @@ void StreamException_ThrowFromErrno( const wxString& streamname, errno_t errcode
throw Exception::AccessDenied( streamname );
case EMFILE: // Too many open files!
throw Exception::CreateStream( streamname, "Too many open files" ); // File handle allocation failure
throw Exception::CannotCreateStream( streamname, "Too many open files" ); // File handle allocation failure
case EEXIST:
throw Exception::CreateStream( streamname, "File already exists" );
throw Exception::CannotCreateStream( streamname, "File already exists" );
case ENOENT: // File not found!
throw Exception::FileNotFound( streamname );
@ -70,7 +70,7 @@ void StreamException_ThrowLastError( const wxString& streamname, HANDLE result )
throw Exception::FileNotFound( streamname );
case ERROR_TOO_MANY_OPEN_FILES:
throw Exception::CreateStream( streamname, "Too many open files" );
throw Exception::CannotCreateStream( streamname, "Too many open files" );
case ERROR_ACCESS_DENIED:
throw Exception::AccessDenied( streamname );

View File

@ -226,7 +226,7 @@ WinPipeRedirection::WinPipeRedirection( FILE* stdstream )
Cleanup();
throw Exception::RuntimeError( ex.FormatDiagnosticMessage(), ex.FormatDisplayMessage() );
}
catch( Exception::BaseException& ex )
catch( BaseException& ex )
{
Cleanup();
ex.DiagMsg() = (wxString)((stdstream==stdout) ? L"STDOUT" : L"STDERR") + L" Redirection Init failed: " + ex.DiagMsg();