diff --git a/common/build/Utilities/Utilities.cbp b/common/build/Utilities/Utilities.cbp
index 42f4375626..53b55da43b 100644
--- a/common/build/Utilities/Utilities.cbp
+++ b/common/build/Utilities/Utilities.cbp
@@ -156,12 +156,14 @@
+
+
@@ -182,6 +184,7 @@
+
diff --git a/common/build/Utilities/utilities.vcproj b/common/build/Utilities/utilities.vcproj
index a035c6d7a3..8d3bb79d7b 100644
--- a/common/build/Utilities/utilities.vcproj
+++ b/common/build/Utilities/utilities.vcproj
@@ -207,10 +207,6 @@
RelativePath="..\..\src\Utilities\Console.cpp"
>
-
-
@@ -426,6 +422,10 @@
RelativePath="..\..\src\Utilities\Mutex.cpp"
>
+
+
@@ -501,10 +501,6 @@
RelativePath="..\..\include\Utilities\Path.h"
>
-
-
@@ -513,6 +509,10 @@
RelativePath="..\..\include\Utilities\pxCheckBox.h"
>
+
+
@@ -556,6 +556,14 @@
+
+
+
+
diff --git a/common/include/Utilities/Console.h b/common/include/Utilities/Console.h
index 11855c261c..b820f4ec0d 100644
--- a/common/include/Utilities/Console.h
+++ b/common/include/Utilities/Console.h
@@ -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;
}
};
diff --git a/common/include/Utilities/EventSource.inl b/common/include/Utilities/EventSource.inl
index 58a46f86fd..f87f19c5c1 100644
--- a/common/include/Utilities/EventSource.inl
+++ b/common/include/Utilities/EventSource.inl
@@ -15,6 +15,8 @@
#pragma once
+#include "Threading.h"
+
using Threading::ScopedLock;
template< typename ListenerType >
@@ -77,7 +79,7 @@ __forceinline void EventSource::_DispatchRaw( ListenerIterator ite
Console.Error( L"Ignoring runtime error thrown from event listener: " + ex.FormatDiagnosticMessage() );
}
}
- catch( Exception::BaseException& ex )
+ catch( BaseException& ex )
{
if( IsDevBuild )
{
diff --git a/common/include/Utilities/Exceptions.h b/common/include/Utilities/Exceptions.h
index a645438a92..45cd0fa204 100644
--- a/common/include/Utilities/Exceptions.h
+++ b/common/include/Utilities/Exceptions.h
@@ -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;
diff --git a/common/include/Utilities/General.h b/common/include/Utilities/General.h
index c40128dd25..61e3c76025 100644
--- a/common/include/Utilities/General.h
+++ b/common/include/Utilities/General.h
@@ -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; }
diff --git a/common/include/Utilities/Path.h b/common/include/Utilities/Path.h
index f4a70fde13..02aacd602f 100644
--- a/common/include/Utilities/Path.h
+++ b/common/include/Utilities/Path.h
@@ -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(); }
diff --git a/common/include/Utilities/PersistentThread.h b/common/include/Utilities/PersistentThread.h
index fd72cdd213..e0bc599c64 100644
--- a/common/include/Utilities/PersistentThread.h
+++ b/common/include/Utilities/PersistentThread.h
@@ -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 m_except;
+ ScopedPtr m_except;
EventSource 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 );
diff --git a/common/include/Utilities/RwMutex.h b/common/include/Utilities/RwMutex.h
new file mode 100644
index 0000000000..2ffd3029b5
--- /dev/null
+++ b/common/include/Utilities/RwMutex.h
@@ -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 .
+ */
+
+#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 );
+ };
+}
diff --git a/common/include/Utilities/Threading.h b/common/include/Utilities/Threading.h
index c8f9fc3a58..30398313d5 100644
--- a/common/include/Utilities/Threading.h
+++ b/common/include/Utilities/Threading.h
@@ -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; }
};
diff --git a/common/include/Utilities/wxAppWithHelpers.h b/common/include/Utilities/wxAppWithHelpers.h
index e304089bb9..a9e37a94d5 100644
--- a/common/include/Utilities/wxAppWithHelpers.h
+++ b/common/include/Utilities/wxAppWithHelpers.h
@@ -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 m_PingWhenIdle;
- std::vector m_DeleteWhenIdle;
std::vector 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
diff --git a/common/include/Utilities/wxGuiTools.h b/common/include/Utilities/wxGuiTools.h
index 2ee9535599..7102746eda 100644
--- a/common/include/Utilities/wxGuiTools.h
+++ b/common/include/Utilities/wxGuiTools.h
@@ -291,6 +291,10 @@ namespace pxSizerFlags
extern wxSizerFlags Checkbox();
};
+BEGIN_DECLARE_EVENT_TYPES()
+ DECLARE_EVENT_TYPE( pxEvt_OnThreadCleanup, -1 );
+END_DECLARE_EVENT_TYPES()
+
// --------------------------------------------------------------------------------------
// wxDialogWithHelpers
// --------------------------------------------------------------------------------------
diff --git a/common/src/Utilities/Console.cpp b/common/src/Utilities/Console.cpp
index 281fe51363..889e9a65b8 100644
--- a/common/src/Utilities/Console.cpp
+++ b/common/src/Utilities/Console.cpp
@@ -292,10 +292,11 @@ class FormatBuffer : public Mutex
public:
bool& clearbit;
SafeArray 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 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 localbuf( 4096, L"Temporary Unicode Formatting Buffer" );
diff --git a/common/src/Utilities/Exceptions.cpp b/common/src/Utilities/Exceptions.cpp
index 9b48055007..f9a843ea37 100644
--- a/common/src/Utilities/Exceptions.cpp
+++ b/common/src/Utilities/Exceptions.cpp
@@ -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
{
diff --git a/common/src/Utilities/Mutex.cpp b/common/src/Utilities/Mutex.cpp
index dcab07e4a8..9015cb592f 100644
--- a/common/src/Utilities/Mutex.cpp
+++ b/common/src/Utilities/Mutex.cpp
@@ -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(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(&locker);
+ if( !m_lock ) return;
+ m_IsLocked = isTryLock ? m_lock->TryAcquire() : false;
+}
diff --git a/common/src/Utilities/RwMutex.cpp b/common/src/Utilities/RwMutex.cpp
new file mode 100644
index 0000000000..4a5192d0e2
--- /dev/null
+++ b/common/src/Utilities/RwMutex.cpp
@@ -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 .
+ */
+
+#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;
+}
diff --git a/common/src/Utilities/Semaphore.cpp b/common/src/Utilities/Semaphore.cpp
index 6b5aed6d13..d0e02777ec 100644
--- a/common/src/Utilities/Semaphore.cpp
+++ b/common/src/Utilities/Semaphore.cpp
@@ -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 );
}
diff --git a/common/src/Utilities/StringHelpers.cpp b/common/src/Utilities/StringHelpers.cpp
index 86ccbdfa31..6902c560ea 100644
--- a/common/src/Utilities/StringHelpers.cpp
+++ b/common/src/Utilities/StringHelpers.cpp
@@ -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 )
diff --git a/common/src/Utilities/ThreadTools.cpp b/common/src/Utilities/ThreadTools.cpp
index 6d1fe4c2e4..feb3e9bc34 100644
--- a/common/src/Utilities/ThreadTools.cpp
+++ b/common/src/Utilities/ThreadTools.cpp
@@ -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
diff --git a/common/src/Utilities/ThreadingInternal.h b/common/src/Utilities/ThreadingInternal.h
index 9cddd7510c..52b2614c94 100644
--- a/common/src/Utilities/ThreadingInternal.h
+++ b/common/src/Utilities/ThreadingInternal.h
@@ -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 );
}
diff --git a/common/src/Utilities/wxAppWithHelpers.cpp b/common/src/Utilities/wxAppWithHelpers.cpp
index db109fd319..0e9a448408 100644
--- a/common/src/Utilities/wxAppWithHelpers.cpp
+++ b/common/src/Utilities/wxAppWithHelpers.cpp
@@ -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 exptr( ex ); // auto-delete it after handling.
+ ex->Rethrow();
+ }
+
+ m_state->SetException( ex );
+}
+
+// --------------------------------------------------------------------------------------
+// pxSynchronousCommandEvent
+// --------------------------------------------------------------------------------------
+IMPLEMENT_DYNAMIC_CLASS( pxSynchronousCommandEvent, wxCommandEvent )
+
+pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState* sema, wxEventType commandType, int winid)
+ : wxCommandEvent( pxEvt_SynchronousCommand, winid )
+{
+ m_sync = sema;
+ m_realEvent = commandType;
+}
+
+pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState& sema, wxEventType commandType, int winid)
+ : wxCommandEvent( pxEvt_SynchronousCommand )
+{
+ m_sync = &sema;
+ m_realEvent = commandType;
+}
+
+pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState* sema, const wxCommandEvent& evt )
+ : wxCommandEvent( evt )
+{
+ m_sync = sema;
+ m_realEvent = evt.GetEventType();
+ SetEventType( pxEvt_SynchronousCommand );
+}
+
+pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState& sema, const wxCommandEvent& evt )
+ : wxCommandEvent( evt )
+{
+ m_sync = &sema;
+ m_realEvent = evt.GetEventType();
+ SetEventType( pxEvt_SynchronousCommand );
+}
+
+pxSynchronousCommandEvent::pxSynchronousCommandEvent( const pxSynchronousCommandEvent& src )
+ : wxCommandEvent( src )
+{
+ m_sync = src.m_sync;
+ m_realEvent = src.m_realEvent;
+}
+
+void pxSynchronousCommandEvent::SetException( const BaseException& ex )
+{
+ if( !m_sync ) ex.Rethrow();
+ m_sync->SetException( ex );
+}
+
+void pxSynchronousCommandEvent::SetException( BaseException* ex )
+{
+ if( !m_sync )
+ {
+ ScopedPtr exptr( ex ); // auto-delete it after handling.
+ ex->Rethrow();
+ }
+
+ m_sync->SetException( ex );
+}
+
+// --------------------------------------------------------------------------------------
+// pxInvokeMethodEvent
+// --------------------------------------------------------------------------------------
+// Unlike pxPingEvent, the Semaphore belonging to this event is typically posted when the
+// invoked method is completed. If the method can be executed in non-blocking fashion then
+// it should leave the semaphore postback NULL.
+//
+class pxInvokeMethodEvent : public pxInvokeActionEvent
+{
+ DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxInvokeMethodEvent)
+
+ typedef pxInvokeActionEvent _parent;
+
+protected:
+ void (*m_Method)();
+
+public:
+ virtual ~pxInvokeMethodEvent() throw() { }
+ virtual pxInvokeMethodEvent *Clone() const { return new pxInvokeMethodEvent(*this); }
+
+ explicit pxInvokeMethodEvent( void (*method)()=NULL, SynchronousActionState* sema=NULL )
+ : pxInvokeActionEvent( sema )
+ {
+ m_Method = method;
+ }
+
+ explicit pxInvokeMethodEvent( void (*method)(), SynchronousActionState& sema )
+ : pxInvokeActionEvent( sema )
+ {
+ m_Method = method;
+ }
+
+ pxInvokeMethodEvent( const pxInvokeMethodEvent& src )
+ : pxInvokeActionEvent( src )
+ {
+ m_Method = src.m_Method;
+ }
+
+ void SetMethod( void (*method)() )
+ {
+ m_Method = method;
+ }
+
+protected:
+ void _DoInvoke()
+ {
+ if( m_Method ) m_Method();
+ }
+};
+
+IMPLEMENT_DYNAMIC_CLASS( pxInvokeMethodEvent, pxInvokeActionEvent )
+
+// --------------------------------------------------------------------------------------
+// pxExceptionEvent implementations
+// --------------------------------------------------------------------------------------
+pxExceptionEvent::pxExceptionEvent( const BaseException& ex )
+{
+ m_except = ex.Clone();
+}
+
+void pxExceptionEvent::_DoInvoke()
+{
+ ScopedPtr deleteMe( m_except );
+ if( deleteMe ) deleteMe->Rethrow();
}
// --------------------------------------------------------------------------------------
@@ -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(evt) );
+}
+
+bool wxAppWithHelpers::ProcessEvent( wxEvent& evt )
+{
+ // Note: We can't do an automatic blocking post of the message here, because wxWidgets
+ // isn't really designed for it (some events return data to the caller via the event
+ // struct, and posting the event would require a temporary clone, where changes would
+ // be lost).
+
+ AffinityAssert_AllowFrom_MainUI();
+ return _parent::ProcessEvent( evt );
+}
+
+bool wxAppWithHelpers::ProcessEvent( wxEvent* evt )
+{
+ AffinityAssert_AllowFrom_MainUI();
+ ScopedPtr deleteMe( evt );
+ return _parent::ProcessEvent( *deleteMe );
+}
+
+bool wxAppWithHelpers::ProcessEvent( pxInvokeActionEvent& evt )
+{
+ if( wxThread::IsMain() )
+ return _parent::ProcessEvent( evt );
+ else
+ {
+ SynchronousActionState sync;
+ evt.SetSyncState( sync );
+ AddPendingEvent( evt );
+ sync.WaitForResult();
+ return true;
+ }
+}
+
+bool wxAppWithHelpers::ProcessEvent( pxInvokeActionEvent* evt )
+{
+ if( wxThread::IsMain() )
+ {
+ ScopedPtr deleteMe( evt );
+ return _parent::ProcessEvent( *deleteMe );
+ }
+ else
+ {
+ SynchronousActionState sync;
+ evt->SetSyncState( sync );
+ AddPendingEvent( *evt );
+ sync.WaitForResult();
+ return true;
+ }
+}
+
+
void wxAppWithHelpers::CleanUp()
{
- 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 %u listeners.", action, size );
-
- for( size_t i=0; iPost();
- }
-
- 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 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
diff --git a/common/src/Utilities/wxHelpers.cpp b/common/src/Utilities/wxHelpers.cpp
index fb26453980..9fe6a2d228 100644
--- a/common/src/Utilities/wxHelpers.cpp
+++ b/common/src/Utilities/wxHelpers.cpp
@@ -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 )
{
diff --git a/common/vsprops/pthreads.vsprops b/common/vsprops/pthreads.vsprops
index c380bec6bf..124fe3b05a 100644
--- a/common/vsprops/pthreads.vsprops
+++ b/common/vsprops/pthreads.vsprops
@@ -12,6 +12,6 @@
/>
diff --git a/pcsx2/CDVD/CDVD.cpp b/pcsx2/CDVD/CDVD.cpp
index 2f58890e33..0a2ea6f5cd 100644
--- a/pcsx2/CDVD/CDVD.cpp
+++ b/pcsx2/CDVD/CDVD.cpp
@@ -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 )
diff --git a/pcsx2/CDVD/CDVDaccess.cpp b/pcsx2/CDVD/CDVDaccess.cpp
index 448cc12941..889b5a1acc 100644
--- a/pcsx2/CDVD/CDVDaccess.cpp
+++ b/pcsx2/CDVD/CDVDaccess.cpp
@@ -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 )
{
diff --git a/pcsx2/CDVD/IsoFS/IsoFS.cpp b/pcsx2/CDVD/IsoFS/IsoFS.cpp
index fd580277f1..3939d787e5 100644
--- a/pcsx2/CDVD/IsoFS/IsoFS.cpp
+++ b/pcsx2/CDVD/IsoFS/IsoFS.cpp
@@ -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 );
diff --git a/pcsx2/Config.h b/pcsx2/Config.h
index 5c21dfc55f..9fbeb199b0 100644
--- a/pcsx2/Config.h
+++ b/pcsx2/Config.h
@@ -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,
diff --git a/pcsx2/Counters.cpp b/pcsx2/Counters.cpp
index e57f046900..0455fe7752 100644
--- a/pcsx2/Counters.cpp
+++ b/pcsx2/Counters.cpp
@@ -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;
}
diff --git a/pcsx2/GSState.cpp b/pcsx2/GSState.cpp
index 66e2b34fa6..6c71c68850 100644
--- a/pcsx2/GSState.cpp
+++ b/pcsx2/GSState.cpp
@@ -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
diff --git a/pcsx2/Linux/pcsx2.cbp b/pcsx2/Linux/pcsx2.cbp
index e3b99e1e30..a0e2b8488e 100644
--- a/pcsx2/Linux/pcsx2.cbp
+++ b/pcsx2/Linux/pcsx2.cbp
@@ -287,7 +287,6 @@
-
@@ -338,6 +337,9 @@
+
+
+
@@ -345,6 +347,8 @@
+
+
@@ -376,6 +380,7 @@
+
@@ -408,7 +413,6 @@
-
@@ -491,9 +495,13 @@
+
+
+
+
diff --git a/pcsx2/MTGS.cpp b/pcsx2/MTGS.cpp
index ed5c3f6c57..059d804fac 100644
--- a/pcsx2/MTGS.cpp
+++ b/pcsx2/MTGS.cpp
@@ -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();
diff --git a/pcsx2/PathDefs.h b/pcsx2/PathDefs.h
index f4224b3e46..8159ab53fd 100644
--- a/pcsx2/PathDefs.h
+++ b/pcsx2/PathDefs.h
@@ -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 );
diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp
index 84763e3641..4b033f4fe9 100644
--- a/pcsx2/Pcsx2Config.cpp
+++ b/pcsx2/Pcsx2Config.cpp
@@ -275,7 +275,7 @@ void Pcsx2Config::LoadSave( IniInterface& ini )
IniBitBool( EnablePatches );
IniBitBool( ConsoleToStdio );
- IniBitBool( SkipBiosSplash );
+ IniBitBool( UseBOOT2Injection );
IniBitBool( McdEnableEjection );
IniBitBool( MultitapPort0_Enabled );
diff --git a/pcsx2/PluginManager.cpp b/pcsx2/PluginManager.cpp
index 614be9fcdf..10bc7f6c99 100644
--- a/pcsx2/PluginManager.cpp
+++ b/pcsx2/PluginManager.cpp
@@ -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(&evt) );
+ if( pi->id != PluginId_PAD && m_info[pi->id] )
+ m_info[pi->id]->CommonBindings.KeyEvent( const_cast(&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; iid] = 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");
}
diff --git a/pcsx2/Plugins.h b/pcsx2/Plugins.h
index 9b52609701..e021bdccff 100644
--- a/pcsx2/Plugins.h
+++ b/pcsx2/Plugins.h
@@ -21,6 +21,8 @@
#include "PS2Edefs.h"
#include "PluginCallbacks.h"
+#include "Utilities/Threading.h"
+
#include
#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 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:
diff --git a/pcsx2/R5900.cpp b/pcsx2/R5900.cpp
index 2ccd360e71..9da571d590 100644
--- a/pcsx2/R5900.cpp
+++ b/pcsx2/R5900.cpp
@@ -82,7 +82,7 @@ void cpuReset()
psxReset();
g_GameStarted = false;
- g_SkipBiosHack = EmuConfig.SkipBiosSplash;
+ g_SkipBiosHack = EmuConfig.UseBOOT2Injection;
ElfCRC = 0;
DiscID = L"";
diff --git a/pcsx2/RecoverySystem.cpp b/pcsx2/RecoverySystem.cpp
deleted file mode 100644
index 2ef1ab7a1a..0000000000
--- a/pcsx2/RecoverySystem.cpp
+++ /dev/null
@@ -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 .
- */
-
-#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;
-static EventSource m_evtsrc_SaveState;
-
-// Used to hold the current state backup (fullcopy of PS2 memory and plugin states).
-static SafeArray 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 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* 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();
-}
diff --git a/pcsx2/SaveState.cpp b/pcsx2/SaveState.cpp
index 57ca5462bc..016def9929 100644
--- a/pcsx2/SaveState.cpp
+++ b/pcsx2/SaveState.cpp
@@ -47,8 +47,18 @@ wxString SaveStateBase::GetFilename( int slot )
}
SaveStateBase::SaveStateBase( SafeArray& memblock )
- : m_memory( memblock )
{
+ Init( &memblock );
+}
+
+SaveStateBase::SaveStateBase( SafeArray* memblock )
+{
+ Init( memblock );
+}
+
+void SaveStateBase::Init( SafeArray* memblock )
+{
+ m_memory = memblock;
m_version = g_SaveVersion;
m_idx = 0;
m_sectid = FreezeId_Unknown;
@@ -58,12 +68,14 @@ SaveStateBase::SaveStateBase( SafeArray& 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& save_to )
{
}
+memSavingState::memSavingState( SafeArray* 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& load_from )
{
}
+memLoadingState::memLoadingState( const SafeArray* load_from )
+ : SaveStateBase( const_cast*>(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 );
}
diff --git a/pcsx2/SaveState.h b/pcsx2/SaveState.h
index 29b5fb52f4..3ad12c9ddd 100644
--- a/pcsx2/SaveState.h
+++ b/pcsx2/SaveState.h
@@ -110,7 +110,7 @@ namespace Exception
class SaveStateBase
{
protected:
- SafeArray& m_memory;
+ SafeArray* m_memory;
char m_tagspace[32];
u32 m_version; // version of the savestate being loaded.
@@ -123,6 +123,7 @@ protected:
public:
SaveStateBase( SafeArray& memblock );
+ SaveStateBase( SafeArray* 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* memblock );
// Load/Save functions for the various components of our glorious emulator!
@@ -233,6 +235,7 @@ protected:
public:
virtual ~memSavingState() throw() { }
memSavingState( SafeArray& save_to );
+ memSavingState( SafeArray* 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& load_from );
+ memLoadingState( const SafeArray* 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(); }
};
diff --git a/pcsx2/System.cpp b/pcsx2/System.cpp
index 65f8da2a98..e5bfa0eb13 100644
--- a/pcsx2/System.cpp
+++ b/pcsx2/System.cpp
@@ -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(EmuConfig.GS);
}
ConsoleLogFilters& SetConsoleConfig()
{
//DbgCon.WriteLn( "Direct modification of EmuConfig.Log detected" );
- AffinityAssert_AllowFromMain();
+ AffinityAssert_AllowFrom_MainUI();
return const_cast(EmuConfig.Log);
}
TraceLogFilters& SetTraceConfig()
{
//DbgCon.WriteLn( "Direct modification of EmuConfig.TraceLog detected" );
- AffinityAssert_AllowFromMain();
+ AffinityAssert_AllowFrom_MainUI();
return const_cast(EmuConfig.Trace);
}
diff --git a/pcsx2/System/SysCoreThread.cpp b/pcsx2/System/SysCoreThread.cpp
index 4f66782268..da7a14219b 100644
--- a/pcsx2/System/SysCoreThread.cpp
+++ b/pcsx2/System/SysCoreThread.cpp
@@ -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(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();
diff --git a/pcsx2/System/SysThreadBase.cpp b/pcsx2/System/SysThreadBase.cpp
index 5846e94bba..970e73be17 100644
--- a/pcsx2/System/SysThreadBase.cpp
+++ b/pcsx2/System/SysThreadBase.cpp
@@ -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 );
diff --git a/pcsx2/System/SysThreads.h b/pcsx2/System/SysThreads.h
index e4d6879ae1..4e27871e3d 100644
--- a/pcsx2/System/SysThreads.h
+++ b/pcsx2/System/SysThreads.h
@@ -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
{
- 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;
diff --git a/pcsx2/ZipTools/FolderDesc.txt b/pcsx2/ZipTools/FolderDesc.txt
new file mode 100644
index 0000000000..37469bfd3f
--- /dev/null
+++ b/pcsx2/ZipTools/FolderDesc.txt
@@ -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.
\ No newline at end of file
diff --git a/pcsx2/ZipTools/ThreadedZipTools.h b/pcsx2/ZipTools/ThreadedZipTools.h
new file mode 100644
index 0000000000..e09e92e413
--- /dev/null
+++ b/pcsx2/ZipTools/ThreadedZipTools.h
@@ -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 .
+ */
+
+#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* 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* srcdata, FnType_WriteCompressedHeader* writeHeader=NULL );
+ CompressThread_gzip( const wxString& file, ScopedPtr>& srcdata, FnType_WriteCompressedHeader* writeHeader=NULL );
+ virtual ~CompressThread_gzip() throw();
+
+protected:
+ void Write( const void* data, size_t size );
+ void ExecuteTaskInThread();
+ void OnCleanupInThread();
+};
diff --git a/pcsx2/ZipTools/thread_gzip.cpp b/pcsx2/ZipTools/thread_gzip.cpp
new file mode 100644
index 0000000000..64932a536c
--- /dev/null
+++ b/pcsx2/ZipTools/thread_gzip.cpp
@@ -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 .
+ */
+
+#include "PrecompiledHeader.h"
+
+#include "App.h"
+#include "SaveState.h"
+#include "ThreadedZipTools.h"
+
+
+CompressThread_gzip::CompressThread_gzip( const wxString& file, SafeArray* srcdata, FnType_WriteCompressedHeader* writeheader )
+ : BaseCompressThread( file, srcdata, writeheader )
+{
+ m_gzfp = NULL;
+}
+
+CompressThread_gzip::CompressThread_gzip( const wxString& file, ScopedPtr>& 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 );
+}
+
diff --git a/pcsx2/ZipTools/thread_lzma.cpp b/pcsx2/ZipTools/thread_lzma.cpp
new file mode 100644
index 0000000000..5a8c41b1d0
--- /dev/null
+++ b/pcsx2/ZipTools/thread_lzma.cpp
@@ -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 .
+ */
+
+#include "PrecompiledHeader.h"
+
diff --git a/pcsx2/gui/App.h b/pcsx2/gui/App.h
index 3aa92f091d..9bf0edff8c 100644
--- a/pcsx2/gui/App.h
+++ b/pcsx2/gui/App.h
@@ -21,7 +21,10 @@
#include
#include
+#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 m_RecentIsoList;
ScopedPtr 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 m_CoreAllocs;
- ScopedPtr 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 m_plugstore;
- const SafeArray* 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();
diff --git a/pcsx2/gui/AppConfig.cpp b/pcsx2/gui/AppConfig.cpp
index 1fc7399fc0..6a8c890d89 100644
--- a/pcsx2/gui/AppConfig.cpp
+++ b/pcsx2/gui/AppConfig.cpp
@@ -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()
{
diff --git a/pcsx2/gui/AppConfig.h b/pcsx2/gui/AppConfig.h
index 4dde838970..6b90fc51f2 100644
--- a/pcsx2/gui/AppConfig.h
+++ b/pcsx2/gui/AppConfig.h
@@ -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 );
diff --git a/pcsx2/gui/AppCorePlugins.cpp b/pcsx2/gui/AppCorePlugins.cpp
new file mode 100644
index 0000000000..a6d2be410a
--- /dev/null
+++ b/pcsx2/gui/AppCorePlugins.cpp
@@ -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 .
+ */
+
+#include "PrecompiledHeader.h"
+#include "App.h"
+#include "AppSaveStates.h"
+#include "GSFrame.h"
+
+#include
+#include
+
+#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 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;
+}
diff --git a/pcsx2/gui/AppCorePlugins.h b/pcsx2/gui/AppCorePlugins.h
new file mode 100644
index 0000000000..dd067004c0
--- /dev/null
+++ b/pcsx2/gui/AppCorePlugins.h
@@ -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 .
+ */
+
+#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();
+};
diff --git a/pcsx2/gui/AppCoreThread.cpp b/pcsx2/gui/AppCoreThread.cpp
index b1caf0f36c..9205ad29a8 100644
--- a/pcsx2/gui/AppCoreThread.cpp
+++ b/pcsx2/gui/AppCoreThread.cpp
@@ -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();
+}
diff --git a/pcsx2/gui/AppCoreThread.h b/pcsx2/gui/AppCoreThread.h
new file mode 100644
index 0000000000..93c6cd7632
--- /dev/null
+++ b/pcsx2/gui/AppCoreThread.h
@@ -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 .
+ */
+
+#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 m_scoped_core;
+
+public:
+ ScopedCoreThreadPopup();
+ virtual ~ScopedCoreThreadPopup() throw() {}
+
+ virtual void AllowResume();
+ virtual void DisallowResume();
+};
diff --git a/pcsx2/gui/AppEventListeners.h b/pcsx2/gui/AppEventListeners.h
index eacf45019e..485797659a 100644
--- a/pcsx2/gui/AppEventListeners.h
+++ b/pcsx2/gui/AppEventListeners.h
@@ -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();
+};
diff --git a/pcsx2/gui/AppEventSources.cpp b/pcsx2/gui/AppEventSources.cpp
index 3dbff37c99..32c19f9724 100644
--- a/pcsx2/gui/AppEventSources.cpp
+++ b/pcsx2/gui/AppEventSources.cpp
@@ -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 );
+}
diff --git a/pcsx2/gui/AppForwardDefs.h b/pcsx2/gui/AppForwardDefs.h
index e735e06482..ec65b4f99c 100644
--- a/pcsx2/gui/AppForwardDefs.h
+++ b/pcsx2/gui/AppForwardDefs.h
@@ -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
diff --git a/pcsx2/gui/AppInit.cpp b/pcsx2/gui/AppInit.cpp
index f852594101..46e24bc6e4 100644
--- a/pcsx2/gui/AppInit.cpp
+++ b/pcsx2/gui/AppInit.cpp
@@ -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;
diff --git a/pcsx2/gui/AppMain.cpp b/pcsx2/gui/AppMain.cpp
index 96c522b9bb..7283108a76 100644
--- a/pcsx2/gui/AppMain.cpp
+++ b/pcsx2/gui/AppMain.cpp
@@ -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 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 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();
-}
-
diff --git a/pcsx2/gui/AppSaveStates.h b/pcsx2/gui/AppSaveStates.h
index cde64b4764..516442396c 100644
--- a/pcsx2/gui/AppSaveStates.h
+++ b/pcsx2/gui/AppSaveStates.h
@@ -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
+// 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 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& 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* StateCopy_GetBuffer();
diff --git a/pcsx2/gui/ApplyState.h b/pcsx2/gui/ApplyState.h
index 9fa0b5beea..2ba63763b5 100644
--- a/pcsx2/gui/ApplyState.h
+++ b/pcsx2/gui/ApplyState.h
@@ -179,5 +179,7 @@ public:
);
virtual ~ApplicableWizardPage() throw() { m_ApplyState.DoCleanup(); }
+
+ virtual bool PrepForApply();
};
diff --git a/pcsx2/gui/ConsoleLogger.h b/pcsx2/gui/ConsoleLogger.h
index b5e216aad4..7e258ebfd0 100644
--- a/pcsx2/gui/ConsoleLogger.h
+++ b/pcsx2/gui/ConsoleLogger.h
@@ -101,7 +101,7 @@ protected:
int m_win32_LinesPerPage;
int m_win32_LinesPerScroll;
#endif
- bool m_IsPaused;
+ ScopedPtr 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 m_QueueColorSection;
diff --git a/pcsx2/gui/Dialogs/FirstTimeWizard.cpp b/pcsx2/gui/Dialogs/FirstTimeWizard.cpp
index cbaa075573..e47153b7ad 100644
--- a/pcsx2/gui/Dialogs/FirstTimeWizard.cpp
+++ b/pcsx2/gui/Dialogs/FirstTimeWizard.cpp
@@ -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;
diff --git a/pcsx2/gui/Dialogs/ModalPopups.h b/pcsx2/gui/Dialogs/ModalPopups.h
index ab4e39243e..1d13e6811f 100644
--- a/pcsx2/gui/Dialogs/ModalPopups.h
+++ b/pcsx2/gui/Dialogs/ModalPopups.h
@@ -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:
diff --git a/pcsx2/gui/Dialogs/PickUserModeDialog.cpp b/pcsx2/gui/Dialogs/PickUserModeDialog.cpp
index 5d13a10b76..30bef6088d 100644
--- a/pcsx2/gui/Dialogs/PickUserModeDialog.cpp
+++ b/pcsx2/gui/Dialogs/PickUserModeDialog.cpp
@@ -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.") );
diff --git a/pcsx2/gui/ExecutorThread.cpp b/pcsx2/gui/ExecutorThread.cpp
new file mode 100644
index 0000000000..2a3a1feeb8
--- /dev/null
+++ b/pcsx2/gui/ExecutorThread.cpp
@@ -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 .
+ */
+
+#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 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 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 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();
+}
diff --git a/pcsx2/gui/FrameForGS.cpp b/pcsx2/gui/FrameForGS.cpp
index f779141368..cf1dec0c02 100644
--- a/pcsx2/gui/FrameForGS.cpp
+++ b/pcsx2/gui/FrameForGS.cpp
@@ -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 );
diff --git a/pcsx2/gui/GSFrame.h b/pcsx2/gui/GSFrame.h
new file mode 100644
index 0000000000..ba7a2e166e
--- /dev/null
+++ b/pcsx2/gui/GSFrame.h
@@ -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 .
+ */
+
+#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_)
diff --git a/pcsx2/gui/GlobalCommands.cpp b/pcsx2/gui/GlobalCommands.cpp
index 352ba38eea..e828c67c51 100644
--- a/pcsx2/gui/GlobalCommands.cpp
+++ b/pcsx2/gui/GlobalCommands.cpp
@@ -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;
}
diff --git a/pcsx2/gui/IsoDropTarget.cpp b/pcsx2/gui/IsoDropTarget.cpp
index b82269d18f..ba775b103a 100644
--- a/pcsx2/gui/IsoDropTarget.cpp
+++ b/pcsx2/gui/IsoDropTarget.cpp
@@ -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;
}
diff --git a/pcsx2/gui/IsoDropTarget.h b/pcsx2/gui/IsoDropTarget.h
index 64d256ef5f..0fa1cf4636 100644
--- a/pcsx2/gui/IsoDropTarget.h
+++ b/pcsx2/gui/IsoDropTarget.h
@@ -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 );
diff --git a/pcsx2/gui/MSWstuff.cpp b/pcsx2/gui/MSWstuff.cpp
index 419d7d1aa6..36569fe683 100644
--- a/pcsx2/gui/MSWstuff.cpp
+++ b/pcsx2/gui/MSWstuff.cpp
@@ -14,7 +14,8 @@
*/
#include "PrecompiledHeader.h"
-#include "MainFrame.h"
+#include "GSFrame.h"
+
#include "MSWstuff.h"
#include
#include
diff --git a/pcsx2/gui/MainFrame.cpp b/pcsx2/gui/MainFrame.cpp
index 749c67c37a..cdfda44c95 100644
--- a/pcsx2/gui/MainFrame.cpp
+++ b/pcsx2/gui/MainFrame.cpp
@@ -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; iGetName( 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 );
}
diff --git a/pcsx2/gui/MainFrame.h b/pcsx2/gui/MainFrame.h
index e7350b591c..85b8a4c829 100644
--- a/pcsx2/gui/MainFrame.h
+++ b/pcsx2/gui/MainFrame.h
@@ -15,99 +15,11 @@
#pragma once
-#include
-#include
-#include
-
#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
+#include
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 m_listener_plugins;
-// EventListenerHelper_CoreThread m_listener_corethread;
-// EventListenerHelper_AppStatus 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();
diff --git a/pcsx2/gui/MainMenuClicks.cpp b/pcsx2/gui/MainMenuClicks.cpp
index 2ab84b9bcb..6990d30360 100644
--- a/pcsx2/gui/MainMenuClicks.cpp
+++ b/pcsx2/gui/MainMenuClicks.cpp
@@ -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)
diff --git a/pcsx2/gui/MessageBoxes.cpp b/pcsx2/gui/MessageBoxes.cpp
index 2590596f5e..c5ebf19e73 100644
--- a/pcsx2/gui/MessageBoxes.cpp
+++ b/pcsx2/gui/MessageBoxes.cpp
@@ -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;
}
diff --git a/pcsx2/gui/Panels/ConfigurationPanels.h b/pcsx2/gui/Panels/ConfigurationPanels.h
index 5156e5ecae..99900cece8 100644
--- a/pcsx2/gui/Panels/ConfigurationPanels.h
+++ b/pcsx2/gui/Panels/ConfigurationPanels.h
@@ -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:
diff --git a/pcsx2/gui/Panels/DirPickerPanel.cpp b/pcsx2/gui/Panels/DirPickerPanel.cpp
index 63ceff35f9..2727ac769c 100644
--- a/pcsx2/gui/Panels/DirPickerPanel.cpp
+++ b/pcsx2/gui/Panels/DirPickerPanel.cpp
@@ -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 );
}
diff --git a/pcsx2/gui/Panels/MiscPanelStuff.cpp b/pcsx2/gui/Panels/MiscPanelStuff.cpp
index af2649e5b9..879b917fa0 100644
--- a/pcsx2/gui/Panels/MiscPanelStuff.cpp
+++ b/pcsx2/gui/Panels/MiscPanelStuff.cpp
@@ -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 );
}
// --------------------------------------------------------------------------------------
diff --git a/pcsx2/gui/Panels/PluginSelectorPanel.cpp b/pcsx2/gui/Panels/PluginSelectorPanel.cpp
index 4f9e35b0df..391a37c752 100644
--- a/pcsx2/gui/Panels/PluginSelectorPanel.cpp
+++ b/pcsx2/gui/Panels/PluginSelectorPanel.cpp
@@ -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 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 );
diff --git a/pcsx2/gui/Plugins.cpp b/pcsx2/gui/Plugins.cpp
deleted file mode 100644
index 36d9b318f7..0000000000
--- a/pcsx2/gui/Plugins.cpp
+++ /dev/null
@@ -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 .
- */
-
-#include "PrecompiledHeader.h"
-#include "App.h"
-#include "MainFrame.h"
-
-#include
-#include
-
-#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; iFreeze( 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 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 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;
-}
diff --git a/pcsx2/gui/RecentIsoList.cpp b/pcsx2/gui/RecentIsoList.cpp
index 6ef638356b..c7dd1cbfec 100644
--- a/pcsx2/gui/RecentIsoList.cpp
+++ b/pcsx2/gui/RecentIsoList.cpp
@@ -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; iRecentIsoCount;
IniScopedGroup groupie( ini, L"RecentIso" );
for( uint i=0; i.
+ */
+
+#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 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* m_dest_buffer;
+
+public:
+ virtual ~SysExecEvent_DownloadState() throw() {}
+ SysExecEvent_DownloadState* Clone() const { return new SysExecEvent_DownloadState( *this ); }
+ SysExecEvent_DownloadState( SafeArray* 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* 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>& src, const wxString& filename )
+ : m_filename( filename )
+ {
+ m_src_buffer = src.DetachPtr();
+ }
+
+ SysExecEvent_ZipToDisk( SafeArray* 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& 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> zipbuf(new SafeArray( 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();
+}
+
diff --git a/pcsx2/gui/UpdateUI.cpp b/pcsx2/gui/UpdateUI.cpp
new file mode 100644
index 0000000000..5ba1d5dd86
--- /dev/null
+++ b/pcsx2/gui/UpdateUI.cpp
@@ -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 .
+ */
+
+#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 );
+}
+
+
diff --git a/pcsx2/gui/pxEventThread.h b/pcsx2/gui/pxEventThread.h
new file mode 100644
index 0000000000..71353a630e
--- /dev/null
+++ b/pcsx2/gui/pxEventThread.h
@@ -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 .
+ */
+
+#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 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 m_ExecutorTimer;
+ ScopedPtr 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();
+};
+
diff --git a/pcsx2/gui/pxLogTextCtrl.cpp b/pcsx2/gui/pxLogTextCtrl.cpp
index 29c91a1cdd..24b3f65129 100644
--- a/pcsx2/gui/pxLogTextCtrl.cpp
+++ b/pcsx2/gui/pxLogTextCtrl.cpp
@@ -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();
}
diff --git a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj
index ca6f59d9db..d468ae717e 100644
--- a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj
+++ b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj
@@ -1880,6 +1880,10 @@
RelativePath="..\..\gui\AppConfig.cpp"
>
+
+
@@ -1940,6 +1944,10 @@
RelativePath="..\..\gui\CpuUsageProviderMSW.cpp"
>
+
+
@@ -1980,10 +1988,6 @@
RelativePath="..\..\gui\MSWstuff.cpp"
>
-
-
@@ -1993,11 +1997,7 @@
>
-
-
+
+
+
+
@@ -2681,6 +2689,10 @@
RelativePath="..\..\gui\Resources\EmbeddedImage.h"
>
+
+
@@ -2697,6 +2709,10 @@
RelativePath="..\..\gui\MainFrame.h"
>
+
+
@@ -2822,6 +2838,38 @@
>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pcsx2/windows/WinCompressNTFS.cpp b/pcsx2/windows/WinCompressNTFS.cpp
index e831aa2666..0b5fecaf64 100644
--- a/pcsx2/windows/WinCompressNTFS.cpp
+++ b/pcsx2/windows/WinCompressNTFS.cpp
@@ -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 );
diff --git a/pcsx2/windows/WinConsolePipe.cpp b/pcsx2/windows/WinConsolePipe.cpp
index c55b8f5a92..d34ffbd248 100644
--- a/pcsx2/windows/WinConsolePipe.cpp
+++ b/pcsx2/windows/WinConsolePipe.cpp
@@ -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();